unique_web_search 2026.44.0.dev0__tar.gz → 2026.44.0.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/PKG-INFO +8 -6
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/README.md +4 -2
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/pyproject.toml +4 -4
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/metrics.py +5 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/README.md +17 -6
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/base.py +22 -12
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/custom_api.py +107 -8
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/settings.py +1 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/client_settings.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/invocation_stats.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/prompts.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/service.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/_registry.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/exceptions.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/prompts/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/prompts/guidelines.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/prompts/guidelines_with_keywords.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/prompts/rejection_response.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/prompts/system_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/prompts/user_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/argument_screening/service.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/client/http_client.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/cleaning/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/cleaning/character_sanitize.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/cleaning/clean.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/cleaning/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/base.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/llm_guard_judge.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/llm_keyword_redact.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/llm_process.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_and_sanitize_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/keyword_extract_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/page_context.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/sanitize_rules.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/system_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/user_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/settings.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/truncate.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/content_processing/service.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/README.md +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/base.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/basic.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/firecrawl.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/jina.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/registry.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/tavily.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/url_safety/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/crawlers/utils.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/README.md +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/base_config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/base_executor.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/context.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/modes.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/executor.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/prompts/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/prompts/refine_query_system_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/prompts/tool_description.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/prompts/tool_description_for_system_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v1/strategy.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/executor.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/prompts/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/prompts/step_query_description.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/prompts/tool_description.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/prompts/tool_description_for_system_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v2/strategy.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/config.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/executor.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/prompts/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/prompts/tool_description.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/prompts/tool_description_for_system_prompt.j2 +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/executors/v3/strategy.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/helpers.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/message_log.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/proxy/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/proxy/bridge.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/proxy/mappers.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/query_elicitation.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/bing.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/brave.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/google.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/perplexity.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/registry.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/google/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/client.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/models.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/runner.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/models.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/response_parsing.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/client.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/exceptions.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/gemini.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/response_handler.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/search_engine/vertexai.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/snippet_judge/__init__.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/snippet_judge/prompts.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/snippet_judge/schema.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/snippet_judge/service.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/services/structured_llm.py +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/skills/web-search-v3.md +0 -0
- {unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-web-search
|
|
3
|
-
Version: 2026.44.0.
|
|
3
|
+
Version: 2026.44.0.dev2
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Andreas Hauri, Gustav Hartz, Rami Azouz
|
|
6
6
|
Author-email: Andreas Hauri <andreas@unique.ch>, Gustav Hartz <gustav.hartz.ext@unique.ch>, Rami Azouz <rami.ext@unique.ch>
|
|
@@ -16,7 +16,7 @@ Requires-Dist: tavily-python>=0.7.11,<1
|
|
|
16
16
|
Requires-Dist: unidecode>=1.4.0,<2
|
|
17
17
|
Requires-Dist: azure-ai-projects>=2.3,<3
|
|
18
18
|
Requires-Dist: azure-identity>=1.25.0,<2
|
|
19
|
-
Requires-Dist: unique-toolkit[monitoring]>=2026.44.0.
|
|
19
|
+
Requires-Dist: unique-toolkit[monitoring]>=2026.44.0.dev9,<2026.44.0rc0
|
|
20
20
|
Requires-Dist: azure-core>=1.36.0,<2
|
|
21
21
|
Requires-Dist: google-cloud-aiplatform>=1.128.0,<2
|
|
22
22
|
Requires-Dist: google-auth>=2.43.0,<3
|
|
@@ -25,8 +25,8 @@ Requires-Dist: rapidfuzz>=3.0.0,<4
|
|
|
25
25
|
Requires-Dist: httpx>=0.28.1
|
|
26
26
|
Requires-Dist: certifi>=2025.11.12
|
|
27
27
|
Requires-Dist: jinja2>=3.1.6
|
|
28
|
-
Requires-Dist: unique-search-proxy-core>=2026.44.0.
|
|
29
|
-
Requires-Dist: unique-search-proxy-sdk>=2026.44.0.
|
|
28
|
+
Requires-Dist: unique-search-proxy-core>=2026.44.0.dev4,<2026.44.0rc0
|
|
29
|
+
Requires-Dist: unique-search-proxy-sdk>=2026.44.0.dev2,<2026.44.0rc0
|
|
30
30
|
Requires-Dist: openai>=1.105.0,<3
|
|
31
31
|
Requires-Python: >=3.12, <4.0
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
@@ -45,7 +45,9 @@ Outbound web access should go through **Unique Search Proxy** (`connectors/uniqu
|
|
|
45
45
|
| **Legacy** | URL unset | Direct provider calls from this process (`_legacy_search` / `_legacy_crawl`) |
|
|
46
46
|
|
|
47
47
|
- **New standard engines (Brave, Perplexity) are proxy-only** — legacy raises.
|
|
48
|
-
- **Custom API**
|
|
48
|
+
- **Custom API** uses proxy egress only when
|
|
49
|
+
`FEATURE_FLAG_SEARCH_PROXY_ENABLE_CUSTOM_API_UN_26736` is enabled; otherwise
|
|
50
|
+
the legacy path reports URL-safety outcomes without blocking.
|
|
49
51
|
- Future provider work targets Search Proxy only; legacy is preserved for migrated engines.
|
|
50
52
|
|
|
51
53
|
Packages involved: [`unique-search-proxy-core`](../../connectors/unique_search_proxy/unique_search_proxy_core/README.md) (schemas), [`unique-search-proxy-sdk`](../../connectors/unique_search_proxy/unique_search_proxy_sdk/README.md) (HTTP), [`unique-search-proxy`](../../connectors/unique_search_proxy/unique_search_proxy_client/README.md) (server). System overview: [`connectors/unique_search_proxy/README.md`](../../connectors/unique_search_proxy/README.md).
|
|
@@ -65,7 +67,7 @@ Query → normalised results (URL / title / snippet / optional content). Optiona
|
|
|
65
67
|
| Google | legacy + proxy | Requires scraping by default |
|
|
66
68
|
| Brave | **proxy-only** | Rich snippets; `ExposableParam` knobs |
|
|
67
69
|
| Perplexity | **proxy-only** | Content extraction knobs; `ExposableParam` |
|
|
68
|
-
| Custom API |
|
|
70
|
+
| Custom API | legacy + proxy | Your REST endpoint; proxy mode rejects redirects |
|
|
69
71
|
|
|
70
72
|
Standard engines that live in proxy-core use **`ExposableParam`** (`expose` + `value`) for optional provider knobs. See [Search Engines README](./src/unique_web_search/services/search_engine/README.md).
|
|
71
73
|
|
|
@@ -12,7 +12,9 @@ Outbound web access should go through **Unique Search Proxy** (`connectors/uniqu
|
|
|
12
12
|
| **Legacy** | URL unset | Direct provider calls from this process (`_legacy_search` / `_legacy_crawl`) |
|
|
13
13
|
|
|
14
14
|
- **New standard engines (Brave, Perplexity) are proxy-only** — legacy raises.
|
|
15
|
-
- **Custom API**
|
|
15
|
+
- **Custom API** uses proxy egress only when
|
|
16
|
+
`FEATURE_FLAG_SEARCH_PROXY_ENABLE_CUSTOM_API_UN_26736` is enabled; otherwise
|
|
17
|
+
the legacy path reports URL-safety outcomes without blocking.
|
|
16
18
|
- Future provider work targets Search Proxy only; legacy is preserved for migrated engines.
|
|
17
19
|
|
|
18
20
|
Packages involved: [`unique-search-proxy-core`](../../connectors/unique_search_proxy/unique_search_proxy_core/README.md) (schemas), [`unique-search-proxy-sdk`](../../connectors/unique_search_proxy/unique_search_proxy_sdk/README.md) (HTTP), [`unique-search-proxy`](../../connectors/unique_search_proxy/unique_search_proxy_client/README.md) (server). System overview: [`connectors/unique_search_proxy/README.md`](../../connectors/unique_search_proxy/README.md).
|
|
@@ -32,7 +34,7 @@ Query → normalised results (URL / title / snippet / optional content). Optiona
|
|
|
32
34
|
| Google | legacy + proxy | Requires scraping by default |
|
|
33
35
|
| Brave | **proxy-only** | Rich snippets; `ExposableParam` knobs |
|
|
34
36
|
| Perplexity | **proxy-only** | Content extraction knobs; `ExposableParam` |
|
|
35
|
-
| Custom API |
|
|
37
|
+
| Custom API | legacy + proxy | Your REST endpoint; proxy mode rejects redirects |
|
|
36
38
|
|
|
37
39
|
Standard engines that live in proxy-core use **`ExposableParam`** (`expose` + `value`) for optional provider knobs. See [Search Engines README](./src/unique_web_search/services/search_engine/README.md).
|
|
38
40
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique_web_search"
|
|
3
|
-
version = "2026.44.0.
|
|
3
|
+
version = "2026.44.0.dev2"
|
|
4
4
|
description = ""
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12,<4.0"
|
|
@@ -22,7 +22,7 @@ dependencies = [
|
|
|
22
22
|
"unidecode>=1.4.0,<2",
|
|
23
23
|
"azure-ai-projects>=2.3,<3",
|
|
24
24
|
"azure-identity>=1.25.0,<2",
|
|
25
|
-
"unique-toolkit[monitoring]>=2026.44.0.
|
|
25
|
+
"unique-toolkit[monitoring]>=2026.44.0.dev9,<2026.44.0rc0",
|
|
26
26
|
"azure-core>=1.36.0,<2",
|
|
27
27
|
"google-cloud-aiplatform>=1.128.0,<2",
|
|
28
28
|
"google-auth>=2.43.0,<3",
|
|
@@ -31,8 +31,8 @@ dependencies = [
|
|
|
31
31
|
"httpx>=0.28.1",
|
|
32
32
|
"certifi>=2025.11.12",
|
|
33
33
|
"jinja2>=3.1.6",
|
|
34
|
-
"unique-search-proxy-core>=2026.44.0.
|
|
35
|
-
"unique-search-proxy-sdk>=2026.44.0.
|
|
34
|
+
"unique-search-proxy-core>=2026.44.0.dev4,<2026.44.0rc0",
|
|
35
|
+
"unique-search-proxy-sdk>=2026.44.0.dev2,<2026.44.0rc0",
|
|
36
36
|
"openai>=1.105.0,<3",
|
|
37
37
|
]
|
|
38
38
|
|
|
@@ -10,6 +10,11 @@ search_total = m.counter("search_total", "Search calls by engine", ["engine"])
|
|
|
10
10
|
search_errors = m.counter(
|
|
11
11
|
"search_errors_total", "Search failures", ["engine", "error_type"]
|
|
12
12
|
)
|
|
13
|
+
custom_api_url_safety_report = m.counter(
|
|
14
|
+
"custom_api_url_safety_report_total",
|
|
15
|
+
"Report-only URL safety outcomes for legacy Custom API searches",
|
|
16
|
+
["outcome", "reason_category"],
|
|
17
|
+
)
|
|
13
18
|
|
|
14
19
|
# End-to-End Tool (P1)
|
|
15
20
|
tool_duration = m.histogram(
|
|
@@ -6,7 +6,11 @@ Implementations wrap standard search APIs and grounding (agent) providers behind
|
|
|
6
6
|
|
|
7
7
|
```python
|
|
8
8
|
# SearchEngine.search (services/search_engine/base.py)
|
|
9
|
-
if
|
|
9
|
+
if (
|
|
10
|
+
search_proxy_client_enabled
|
|
11
|
+
and engine is proxy-supported # config.engine is SearchEngineType / AgentEngineType
|
|
12
|
+
and await self._proxy_routing_enabled() # per-engine gate; Custom API checks its feature flag
|
|
13
|
+
):
|
|
10
14
|
return await self._proxy_search(...) # standard → /v1/search; agent → /v1/agent-search
|
|
11
15
|
return await self._legacy_search(...) # direct provider call from this process
|
|
12
16
|
```
|
|
@@ -15,7 +19,7 @@ return await self._legacy_search(...) # direct provider call from this proc
|
|
|
15
19
|
|------|---------|-------|--------|
|
|
16
20
|
| **Standard** | Google | yes | yes |
|
|
17
21
|
| **Standard** | Brave, Perplexity | yes | **raises** (proxy-only) |
|
|
18
|
-
| **Standard** | Custom API |
|
|
22
|
+
| **Standard** | Custom API | yes | yes |
|
|
19
23
|
| **Agent** | Bing, VertexAI | yes | yes |
|
|
20
24
|
|
|
21
25
|
Deployment configs for Google / Brave / Perplexity (and agent base fields) live in **`unique_search_proxy_core`**. This package registers tool wrappers and adds web-only fields where needed (e.g. `requires_scraping` on Bing/VertexAI).
|
|
@@ -127,13 +131,20 @@ Config: `unique_search_proxy_core.search_engines.perplexity.schema.PerplexityCon
|
|
|
127
131
|
PERPLEXITY_API_KEY=...
|
|
128
132
|
```
|
|
129
133
|
|
|
130
|
-
### Custom API Search
|
|
134
|
+
### Custom API Search
|
|
131
135
|
|
|
132
|
-
**Provider:** your REST endpoint · **
|
|
136
|
+
**Provider:** your REST endpoint · **Proxy:** legacy + proxy
|
|
133
137
|
|
|
134
|
-
Config: `unique_web_search.services.search_engine.custom_api.CustomAPIConfig` (`LocalSearchEngineType.CUSTOM_API`)
|
|
138
|
+
Config: `unique_web_search.services.search_engine.custom_api.CustomAPIConfig` (engine `SearchEngineType.CUSTOM_API`; registered under `LocalSearchEngineType.CUSTOM_API`)
|
|
135
139
|
|
|
136
|
-
Response must be JSON with a `results`
|
|
140
|
+
Response must be JSON with a `results` or legacy `curated` array of
|
|
141
|
+
`{url, title, snippet, content?}`. When
|
|
142
|
+
`FEATURE_FLAG_SEARCH_PROXY_ENABLE_CUSTOM_API_UN_26736` is enabled, the endpoint
|
|
143
|
+
is validated against the Search Proxy URL-safety policy, DNS is pinned, and
|
|
144
|
+
redirects are rejected. Exact private hosts can be operator-approved with
|
|
145
|
+
`CUSTOM_WEB_SEARCH_API_TRUSTED_PRIVATE_HOSTS`; this exception applies only to
|
|
146
|
+
Customized API endpoints. Use the same value in assistants-core for accurate
|
|
147
|
+
report-only metrics before proxy routing is enabled.
|
|
137
148
|
|
|
138
149
|
```bash
|
|
139
150
|
CUSTOM_WEB_SEARCH_API_ENDPOINT=https://your-api.example.com/search
|
|
@@ -8,14 +8,14 @@ from pydantic import BaseModel
|
|
|
8
8
|
from unique_search_proxy_core.agent_engines.base import AgentEngineType
|
|
9
9
|
from unique_search_proxy_core.agent_engines.config_types import ENGINE_NAME_TO_CONFIG
|
|
10
10
|
from unique_search_proxy_core.context import LOCAL_REQUEST_CONTEXT, RequestContext
|
|
11
|
+
from unique_search_proxy_core.param_policy.exposable_config import (
|
|
12
|
+
ExposableParamsConfig,
|
|
13
|
+
)
|
|
11
14
|
from unique_search_proxy_core.param_policy.exposable_param import (
|
|
12
15
|
resolve_exposable_value,
|
|
13
16
|
)
|
|
14
17
|
from unique_search_proxy_core.param_policy.exposed_params import ExposedParams
|
|
15
|
-
from unique_search_proxy_core.search_engines.base import
|
|
16
|
-
BaseSearchEngineConfig,
|
|
17
|
-
SearchEngineType,
|
|
18
|
-
)
|
|
18
|
+
from unique_search_proxy_core.search_engines.base import SearchEngineType
|
|
19
19
|
|
|
20
20
|
from unique_web_search.services.proxy.bridge import (
|
|
21
21
|
open_search_proxy_client,
|
|
@@ -45,8 +45,7 @@ class LocalSearchEngineType(StrEnum):
|
|
|
45
45
|
CUSTOM_API = "custom_api"
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
# Engine ids that the search proxy can serve.
|
|
49
|
-
# (e.g. ``LocalSearchEngineType``) is routed to the local legacy implementation.
|
|
48
|
+
# Engine ids that the search proxy can serve.
|
|
50
49
|
ProxyEngineType = SearchEngineType | AgentEngineType
|
|
51
50
|
|
|
52
51
|
# Fields present on agent-engine configs that are not accepted as proxy call kwargs.
|
|
@@ -69,8 +68,7 @@ class SearchEngine(ABC, Generic[SearchEngineConfig]):
|
|
|
69
68
|
provide the direct ``_legacy_search`` implementation.
|
|
70
69
|
|
|
71
70
|
``search`` uses the proxy whenever it is enabled and the engine is supported by
|
|
72
|
-
it, otherwise it reroutes to the local legacy implementation.
|
|
73
|
-
proxy cannot serve (e.g. custom APIs) always fall back to ``_legacy_search``.
|
|
71
|
+
it, otherwise it reroutes to the local legacy implementation.
|
|
74
72
|
"""
|
|
75
73
|
|
|
76
74
|
# Set by agent engines (Bing/VertexAI) and consumed by the agent proxy path.
|
|
@@ -90,6 +88,14 @@ class SearchEngine(ABC, Generic[SearchEngineConfig]):
|
|
|
90
88
|
"""Whether the search engine requires scraping."""
|
|
91
89
|
return False
|
|
92
90
|
|
|
91
|
+
async def _proxy_routing_enabled(self) -> bool:
|
|
92
|
+
"""Whether this engine may use Search Proxy for the current request."""
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def _standard_proxy_client_timeout(self) -> float:
|
|
97
|
+
return _STANDARD_PROXY_TIMEOUT
|
|
98
|
+
|
|
93
99
|
@property
|
|
94
100
|
def _proxy_engine(self) -> ProxyEngineType | None:
|
|
95
101
|
"""The proxy engine id for this config, or ``None`` if not proxy-supported."""
|
|
@@ -105,7 +111,11 @@ class SearchEngine(ABC, Generic[SearchEngineConfig]):
|
|
|
105
111
|
) -> list[WebSearchResult]:
|
|
106
112
|
"""Search the web for the given query using the search engine."""
|
|
107
113
|
proxy_engine = self._proxy_engine
|
|
108
|
-
if
|
|
114
|
+
if (
|
|
115
|
+
search_proxy_client_enabled
|
|
116
|
+
and proxy_engine is not None
|
|
117
|
+
and await self._proxy_routing_enabled()
|
|
118
|
+
):
|
|
109
119
|
return await self._proxy_search(query, params, proxy_engine)
|
|
110
120
|
return await self._legacy_search(query=query, params=params)
|
|
111
121
|
|
|
@@ -127,9 +137,9 @@ class SearchEngine(ABC, Generic[SearchEngineConfig]):
|
|
|
127
137
|
engine: SearchEngineType,
|
|
128
138
|
) -> list[WebSearchResult]:
|
|
129
139
|
"""Merge deployment config with per-call params and dispatch via the proxy SDK."""
|
|
130
|
-
if not isinstance(self.config,
|
|
140
|
+
if not isinstance(self.config, ExposableParamsConfig):
|
|
131
141
|
raise TypeError(
|
|
132
|
-
f"Standard proxy search requires
|
|
142
|
+
f"Standard proxy search requires ExposableParamsConfig, "
|
|
133
143
|
f"got {type(self.config).__name__}"
|
|
134
144
|
)
|
|
135
145
|
overrides = (
|
|
@@ -140,7 +150,7 @@ class SearchEngine(ABC, Generic[SearchEngineConfig]):
|
|
|
140
150
|
invocation.pop("engine", None)
|
|
141
151
|
invocation.pop("query", None)
|
|
142
152
|
async with open_search_proxy_client(
|
|
143
|
-
timeout=
|
|
153
|
+
timeout=self._standard_proxy_client_timeout,
|
|
144
154
|
context=self._request_context,
|
|
145
155
|
) as client:
|
|
146
156
|
response = await client.search.search(
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import json
|
|
2
|
-
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, TypeVar, override
|
|
3
4
|
|
|
4
5
|
from httpx import AsyncClient
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
+
from pydantic import BaseModel, Field, model_validator
|
|
6
7
|
from pydantic.json_schema import SkipJsonSchema
|
|
7
8
|
from unique_search_proxy_core.context import LOCAL_REQUEST_CONTEXT, RequestContext
|
|
8
9
|
from unique_search_proxy_core.param_policy.exposed_params import ExposedParams
|
|
10
|
+
from unique_search_proxy_core.search_engines.custom_api.schema import (
|
|
11
|
+
CustomApiConfig as ProxyCustomApiConfig,
|
|
12
|
+
)
|
|
13
|
+
from unique_search_proxy_core.search_engines.custom_api.schema import (
|
|
14
|
+
CustomApiSearchRequest,
|
|
15
|
+
)
|
|
16
|
+
from unique_search_proxy_core.url_safety import (
|
|
17
|
+
CustomApiTargetValidationError,
|
|
18
|
+
UrlSafetyService,
|
|
19
|
+
)
|
|
20
|
+
from unique_toolkit.agentic.feature_flags import FeatureFlagNames
|
|
9
21
|
from unique_toolkit.agentic.tools.config import get_configuration_dict
|
|
22
|
+
from unique_toolkit.experimental.resources.feature_flags import is_flag_enabled
|
|
10
23
|
|
|
24
|
+
from unique_web_search.metrics import custom_api_url_safety_report
|
|
11
25
|
from unique_web_search.services.search_engine.base import (
|
|
12
26
|
LocalSearchEngineType,
|
|
13
27
|
SearchEngine,
|
|
@@ -20,6 +34,8 @@ from unique_web_search.services.search_engine.schema import (
|
|
|
20
34
|
)
|
|
21
35
|
from unique_web_search.settings import CUSTOM_API_REQUEST_METHOD, env_settings
|
|
22
36
|
|
|
37
|
+
_LOGGER = logging.getLogger(__name__)
|
|
38
|
+
|
|
23
39
|
T = TypeVar("T")
|
|
24
40
|
|
|
25
41
|
|
|
@@ -64,11 +80,9 @@ ApiRequestMethodType, ApiRequestMethodField = conditional_type(
|
|
|
64
80
|
)
|
|
65
81
|
|
|
66
82
|
|
|
67
|
-
class CustomAPIConfig(
|
|
83
|
+
class CustomAPIConfig(ProxyCustomApiConfig):
|
|
68
84
|
model_config = get_configuration_dict(title="Customized API")
|
|
69
85
|
|
|
70
|
-
engine: Literal[LocalSearchEngineType.CUSTOM_API] = LocalSearchEngineType.CUSTOM_API
|
|
71
|
-
|
|
72
86
|
api_endpoint: ApiEndpointType = ApiEndpointField # type: ignore (Dynamic type generation)
|
|
73
87
|
api_headers: ApiHeadersType = ApiHeadersField # type: ignore (Dynamic type generation)
|
|
74
88
|
api_additional_query_params: ApiAdditionalQueryParamsType = ( # type: ignore (Dynamic type generation)
|
|
@@ -88,6 +102,43 @@ class CustomAPIConfig(BaseModel):
|
|
|
88
102
|
)
|
|
89
103
|
timeout: int = Field(default=120, description="The timeout of the custom API")
|
|
90
104
|
|
|
105
|
+
@model_validator(mode="before")
|
|
106
|
+
@classmethod
|
|
107
|
+
def apply_operator_config_precedence(cls, value: Any) -> Any:
|
|
108
|
+
if not isinstance(value, dict):
|
|
109
|
+
return value
|
|
110
|
+
|
|
111
|
+
resolved_value = dict(value)
|
|
112
|
+
operator_values = {
|
|
113
|
+
"apiEndpoint": env_settings.custom_web_search_api_endpoint,
|
|
114
|
+
"apiHeaders": env_settings.custom_web_search_api_headers,
|
|
115
|
+
"apiAdditionalQueryParams": (
|
|
116
|
+
env_settings.custom_web_search_api_additional_query_params
|
|
117
|
+
),
|
|
118
|
+
"apiAdditionalBodyParams": (
|
|
119
|
+
env_settings.custom_web_search_api_additional_body_params
|
|
120
|
+
),
|
|
121
|
+
"apiRequestMethod": env_settings.custom_web_search_api_method,
|
|
122
|
+
}
|
|
123
|
+
aliases = {
|
|
124
|
+
"apiEndpoint": "api_endpoint",
|
|
125
|
+
"apiHeaders": "api_headers",
|
|
126
|
+
"apiAdditionalQueryParams": "api_additional_query_params",
|
|
127
|
+
"apiAdditionalBodyParams": "api_additional_body_params",
|
|
128
|
+
"apiRequestMethod": "api_request_method",
|
|
129
|
+
}
|
|
130
|
+
for alias, operator_value in operator_values.items():
|
|
131
|
+
if operator_value is None:
|
|
132
|
+
continue
|
|
133
|
+
resolved_value.pop(aliases[alias], None)
|
|
134
|
+
resolved_value[alias] = operator_value
|
|
135
|
+
return resolved_value
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def request_model(cls) -> type[BaseModel]:
|
|
139
|
+
"""Use the proxy's wire contract, excluding tool-only configuration fields."""
|
|
140
|
+
return CustomApiSearchRequest
|
|
141
|
+
|
|
91
142
|
|
|
92
143
|
@register_search_engine(
|
|
93
144
|
name="custom_api",
|
|
@@ -107,6 +158,17 @@ class CustomAPI(SearchEngine[CustomAPIConfig]):
|
|
|
107
158
|
self.api_endpoint = config.api_endpoint
|
|
108
159
|
self.is_configured = True # No possibility to check if the API is configured from our side. So we assume it is configured.
|
|
109
160
|
|
|
161
|
+
async def _proxy_routing_enabled(self) -> bool:
|
|
162
|
+
return await is_flag_enabled(
|
|
163
|
+
FeatureFlagNames.search_proxy_enable_custom_api_un_26736,
|
|
164
|
+
company_id=self._request_context.company_id,
|
|
165
|
+
user_id=self._request_context.user_id,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def _standard_proxy_client_timeout(self) -> float:
|
|
170
|
+
return float(self.config.timeout) + 5.0
|
|
171
|
+
|
|
110
172
|
@override
|
|
111
173
|
async def _legacy_search(
|
|
112
174
|
self,
|
|
@@ -114,10 +176,10 @@ class CustomAPI(SearchEngine[CustomAPIConfig]):
|
|
|
114
176
|
params: ExposedParams | None,
|
|
115
177
|
) -> list[WebSearchResult]:
|
|
116
178
|
del params
|
|
179
|
+
await self._report_url_safety()
|
|
117
180
|
params_dict, body = self._prepare_request_params_and_body(query)
|
|
118
|
-
async_client_params = self._client_config
|
|
119
|
-
|
|
120
|
-
}
|
|
181
|
+
async_client_params: dict[str, Any] = dict(self._client_config)
|
|
182
|
+
async_client_params["timeout"] = self.config.timeout
|
|
121
183
|
async with AsyncClient(**async_client_params) as client:
|
|
122
184
|
response = await client.request(
|
|
123
185
|
method=self._request_method,
|
|
@@ -135,6 +197,43 @@ class CustomAPI(SearchEngine[CustomAPIConfig]):
|
|
|
135
197
|
validated_response = WebSearchResults.model_validate(response.json())
|
|
136
198
|
return validated_response.results
|
|
137
199
|
|
|
200
|
+
async def _report_url_safety(self) -> None:
|
|
201
|
+
try:
|
|
202
|
+
await UrlSafetyService.resolve_custom_api_target(
|
|
203
|
+
self.config.api_endpoint,
|
|
204
|
+
trusted_private_hosts=(
|
|
205
|
+
env_settings.custom_web_search_api_trusted_private_hosts
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
except CustomApiTargetValidationError as exc:
|
|
209
|
+
reason_category = exc.blocked_targets[0].category
|
|
210
|
+
custom_api_url_safety_report.labels(
|
|
211
|
+
outcome="would_block",
|
|
212
|
+
reason_category=reason_category,
|
|
213
|
+
).inc()
|
|
214
|
+
_LOGGER.warning(
|
|
215
|
+
"Search Proxy URL safety would block this Custom API endpoint; "
|
|
216
|
+
"continuing through the legacy direct path because proxy routing "
|
|
217
|
+
"is disabled company_id=%s category=%s",
|
|
218
|
+
self._request_context.company_id,
|
|
219
|
+
reason_category,
|
|
220
|
+
)
|
|
221
|
+
except Exception as exc:
|
|
222
|
+
custom_api_url_safety_report.labels(
|
|
223
|
+
outcome="validation_error",
|
|
224
|
+
reason_category="unexpected",
|
|
225
|
+
).inc()
|
|
226
|
+
_LOGGER.warning(
|
|
227
|
+
"Custom API URL safety report failed company_id=%s error_type=%s",
|
|
228
|
+
self._request_context.company_id,
|
|
229
|
+
type(exc).__name__,
|
|
230
|
+
)
|
|
231
|
+
else:
|
|
232
|
+
custom_api_url_safety_report.labels(
|
|
233
|
+
outcome="allowed",
|
|
234
|
+
reason_category="none",
|
|
235
|
+
).inc()
|
|
236
|
+
|
|
138
237
|
@property
|
|
139
238
|
def requires_scraping(self) -> bool:
|
|
140
239
|
return self.config.requires_scraping
|
|
@@ -77,6 +77,7 @@ class Base(BaseSettings):
|
|
|
77
77
|
custom_web_search_api_additional_query_params: str | None = None
|
|
78
78
|
custom_web_search_api_additional_body_params: str | None = None
|
|
79
79
|
custom_web_search_api_client_config: str | None = None
|
|
80
|
+
custom_web_search_api_trusted_private_hosts: list[str] = []
|
|
80
81
|
|
|
81
82
|
# Proxy settings
|
|
82
83
|
## Shared settings
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_web_search-2026.44.0.dev0 → unique_web_search-2026.44.0.dev2}/src/unique_web_search/utils.py
RENAMED
|
File without changes
|