unique_web_search 2026.38.0.dev2__tar.gz → 2026.38.0.dev4__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.38.0.dev2 → unique_web_search-2026.38.0.dev4}/PKG-INFO +3 -3
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/pyproject.toml +3 -3
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/service.py +1 -0
- unique_web_search-2026.38.0.dev4/src/unique_web_search/services/client/http_client.py +70 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/basic.py +25 -24
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/google.py +3 -3
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/settings.py +6 -0
- unique_web_search-2026.38.0.dev2/src/unique_web_search/services/client/proxy_config.py +0 -119
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/README.md +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/client_settings.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/invocation_stats.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/metrics.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/prompts.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/_registry.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/exceptions.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/prompts/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/prompts/guidelines.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/prompts/guidelines_with_keywords.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/prompts/rejection_response.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/prompts/system_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/prompts/user_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/argument_screening/service.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/cleaning/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/cleaning/character_sanitize.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/cleaning/clean.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/cleaning/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/base.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/llm_guard_judge.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/llm_keyword_redact.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/llm_process.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_and_sanitize_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/keyword_extract_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/page_context.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/sanitize_rules.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/system_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/user_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/settings.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/truncate.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/content_processing/service.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/README.md +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/base.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/firecrawl.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/jina.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/registry.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/tavily.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/url_safety/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/crawlers/utils.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/README.md +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/base_config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/base_executor.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/context.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/modes.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/executor.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/prompts/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/prompts/refine_query_system_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/prompts/tool_description.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/prompts/tool_description_for_system_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v1/strategy.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/executor.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/prompts/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/prompts/step_query_description.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/prompts/tool_description.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/prompts/tool_description_for_system_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v2/strategy.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/config.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/executor.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/prompts/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/prompts/tool_description.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/prompts/tool_description_for_system_prompt.j2 +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/executors/v3/strategy.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/helpers.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/message_log.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/proxy/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/proxy/bridge.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/proxy/mappers.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/query_elicitation.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/README.md +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/base.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/bing.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/brave.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/custom_api.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/perplexity.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/registry.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/google/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/client.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/models.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/runner.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/models.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/response_parsing.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/client.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/exceptions.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/gemini.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/response_handler.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/search_engine/vertexai.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/snippet_judge/__init__.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/snippet_judge/prompts.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/snippet_judge/schema.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/snippet_judge/service.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/services/structured_llm.py +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/skills/web-search-v3.md +0 -0
- {unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/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.38.0.
|
|
3
|
+
Version: 2026.38.0.dev4
|
|
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.38.0.
|
|
19
|
+
Requires-Dist: unique-toolkit[monitoring]>=2026.38.0.dev18,<2026.38.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,7 +25,7 @@ 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.38.0.
|
|
28
|
+
Requires-Dist: unique-search-proxy-core>=2026.38.0.dev6,<2026.38.0rc0
|
|
29
29
|
Requires-Dist: unique-search-proxy-sdk>=2026.38.0.dev2,<2026.38.0rc0
|
|
30
30
|
Requires-Dist: openai>=1.105.0,<3
|
|
31
31
|
Requires-Python: >=3.12, <4.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique_web_search"
|
|
3
|
-
version = "2026.38.0.
|
|
3
|
+
version = "2026.38.0.dev4"
|
|
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.38.0.
|
|
25
|
+
"unique-toolkit[monitoring]>=2026.38.0.dev18,<2026.38.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,7 +31,7 @@ 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.38.0.
|
|
34
|
+
"unique-search-proxy-core>=2026.38.0.dev6,<2026.38.0rc0",
|
|
35
35
|
"unique-search-proxy-sdk>=2026.38.0.dev2,<2026.38.0rc0",
|
|
36
36
|
"openai>=1.105.0,<3",
|
|
37
37
|
]
|
|
@@ -80,6 +80,7 @@ class WebSearchTool(Tool[WebSearchConfig]):
|
|
|
80
80
|
company_id=self.event.company_id,
|
|
81
81
|
user_id=self.event.user_id,
|
|
82
82
|
chat_id=self.event.payload.chat_id,
|
|
83
|
+
user_metadata=self.event.payload.user_metadata or {},
|
|
83
84
|
)
|
|
84
85
|
self.search_engine_service = get_search_engine_service(
|
|
85
86
|
self.config.search_engine_config,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Process-wide HTTP client registry for the web-search tool's direct egress path.
|
|
2
|
+
|
|
3
|
+
Clients are created lazily on first use via ``client_for`` / the registry — never
|
|
4
|
+
at import time — so per-user proxy configs without a settings username can load.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from httpx import AsyncClient
|
|
10
|
+
from pydantic import SecretStr
|
|
11
|
+
from unique_search_proxy_core.context import RequestContext
|
|
12
|
+
from unique_search_proxy_core.http_client import (
|
|
13
|
+
HttpClientRegistry,
|
|
14
|
+
ProxySettings,
|
|
15
|
+
resolver_from_settings,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from unique_web_search.settings import env_settings
|
|
19
|
+
|
|
20
|
+
_registry: HttpClientRegistry | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def proxy_settings_from_env() -> ProxySettings:
|
|
24
|
+
"""Build core ProxySettings from the tool's process env settings."""
|
|
25
|
+
return ProxySettings(
|
|
26
|
+
proxy_auth_mode=env_settings.proxy_auth_mode,
|
|
27
|
+
proxy_protocol=env_settings.proxy_protocol,
|
|
28
|
+
proxy_host=env_settings.proxy_host,
|
|
29
|
+
proxy_port=env_settings.proxy_port,
|
|
30
|
+
proxy_headers={
|
|
31
|
+
name: SecretStr(value) for name, value in env_settings.proxy_headers.items()
|
|
32
|
+
},
|
|
33
|
+
proxy_ssl_ca_bundle_path=env_settings.proxy_ssl_ca_bundle_path,
|
|
34
|
+
proxy_username=(
|
|
35
|
+
SecretStr(env_settings.proxy_username)
|
|
36
|
+
if env_settings.proxy_username is not None
|
|
37
|
+
else None
|
|
38
|
+
),
|
|
39
|
+
proxy_password=SecretStr(env_settings.proxy_password or ""),
|
|
40
|
+
proxy_ssl_cert_path=env_settings.proxy_ssl_cert_path,
|
|
41
|
+
proxy_ssl_key_path=env_settings.proxy_ssl_key_path,
|
|
42
|
+
proxy_username_source=env_settings.proxy_username_source,
|
|
43
|
+
proxy_username_metadata_field=env_settings.proxy_username_metadata_field,
|
|
44
|
+
per_user_proxy_company_ids=env_settings.per_user_proxy_company_ids,
|
|
45
|
+
http_client_cache_size=env_settings.http_client_cache_size,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_http_client_registry() -> HttpClientRegistry:
|
|
50
|
+
"""Return the process-wide registry for direct (non-proxy-service) egress."""
|
|
51
|
+
global _registry
|
|
52
|
+
if _registry is None:
|
|
53
|
+
settings = proxy_settings_from_env()
|
|
54
|
+
_registry = HttpClientRegistry(
|
|
55
|
+
settings=settings,
|
|
56
|
+
resolver=resolver_from_settings(settings),
|
|
57
|
+
)
|
|
58
|
+
return _registry
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def client_for(context: RequestContext) -> AsyncClient:
|
|
62
|
+
"""Resolve the egress client for a request context."""
|
|
63
|
+
return await get_http_client_registry().client_for(context)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
__all__ = [
|
|
67
|
+
"client_for",
|
|
68
|
+
"get_http_client_registry",
|
|
69
|
+
"proxy_settings_from_env",
|
|
70
|
+
]
|
|
@@ -18,7 +18,7 @@ from unique_search_proxy_core.crawlers.basic.schema import (
|
|
|
18
18
|
)
|
|
19
19
|
from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
20
20
|
|
|
21
|
-
from unique_web_search.services.client.
|
|
21
|
+
from unique_web_search.services.client.http_client import client_for
|
|
22
22
|
from unique_web_search.services.crawlers.base import BaseCrawler
|
|
23
23
|
from unique_web_search.services.crawlers.registry import register_crawler
|
|
24
24
|
from unique_web_search.services.crawlers.url_safety import (
|
|
@@ -89,29 +89,29 @@ class BasicCrawler(BaseCrawler[BasicConfig]):
|
|
|
89
89
|
|
|
90
90
|
@override
|
|
91
91
|
async def _legacy_crawl(self, targets: list[ResolvedCrawlTarget]) -> list[str]:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
92
|
+
client = await client_for(self._request_context)
|
|
93
|
+
tasks = [self._crawl_url_with_client(client, target) for target in targets]
|
|
94
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
95
|
+
|
|
96
|
+
validation_errors = [
|
|
97
|
+
result
|
|
98
|
+
for result in results
|
|
99
|
+
if isinstance(result, CrawlTargetValidationError)
|
|
100
|
+
]
|
|
101
|
+
if validation_errors:
|
|
102
|
+
raise validation_errors[0]
|
|
103
|
+
|
|
104
|
+
markdowns: list[str] = []
|
|
105
|
+
for result in results:
|
|
106
|
+
if isinstance(result, BaseException):
|
|
107
|
+
markdowns.append(
|
|
108
|
+
f"Unexpected error occurred while crawling the URL: {result}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
else:
|
|
112
|
+
markdowns.append(result)
|
|
113
|
+
|
|
114
|
+
return markdowns
|
|
115
115
|
|
|
116
116
|
async def _crawl_url_with_client(
|
|
117
117
|
self, client: AsyncClient, target: ResolvedCrawlTarget
|
|
@@ -132,6 +132,7 @@ class BasicCrawler(BaseCrawler[BasicConfig]):
|
|
|
132
132
|
target.request_url,
|
|
133
133
|
headers=request_headers,
|
|
134
134
|
extensions=request_extensions or None,
|
|
135
|
+
timeout=Timeout(self.config.timeout),
|
|
135
136
|
)
|
|
136
137
|
|
|
137
138
|
_ = response.raise_for_status()
|
|
@@ -9,7 +9,7 @@ from unique_search_proxy_core.search_engines.base import SearchEngineType
|
|
|
9
9
|
from unique_search_proxy_core.search_engines.google.schema import GoogleConfig
|
|
10
10
|
|
|
11
11
|
from unique_web_search.client_settings import get_google_search_settings
|
|
12
|
-
from unique_web_search.services.client.
|
|
12
|
+
from unique_web_search.services.client.http_client import client_for
|
|
13
13
|
from unique_web_search.services.search_engine.base import SearchEngine, SearchEngineMode
|
|
14
14
|
from unique_web_search.services.search_engine.registry import register_search_engine
|
|
15
15
|
from unique_web_search.services.search_engine.schema import (
|
|
@@ -69,8 +69,8 @@ class GoogleSearch(SearchEngine[GoogleConfig]):
|
|
|
69
69
|
start_index=start_index,
|
|
70
70
|
num_fetch=num_fetch,
|
|
71
71
|
)
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
client = await client_for(self._request_context)
|
|
73
|
+
response = await client.get(**request_params)
|
|
74
74
|
return response
|
|
75
75
|
|
|
76
76
|
def _validate_url(self, url: str) -> bool:
|
|
@@ -93,6 +93,12 @@ class Base(BaseSettings):
|
|
|
93
93
|
proxy_username: str | None = None
|
|
94
94
|
proxy_password: str | None = None
|
|
95
95
|
|
|
96
|
+
## Username source for per-user corporate proxies
|
|
97
|
+
proxy_username_source: Literal["settings", "user_metadata"] = "settings"
|
|
98
|
+
proxy_username_metadata_field: str = "userName"
|
|
99
|
+
per_user_proxy_company_ids: list[str] = []
|
|
100
|
+
http_client_cache_size: int = 128
|
|
101
|
+
|
|
96
102
|
## For SSL/TLS authentication
|
|
97
103
|
proxy_ssl_cert_path: str | None = None
|
|
98
104
|
proxy_ssl_key_path: str | None = None
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from functools import partial
|
|
3
|
-
|
|
4
|
-
from httpx import AsyncClient
|
|
5
|
-
from pydantic import BaseModel
|
|
6
|
-
|
|
7
|
-
from unique_web_search.settings import env_settings
|
|
8
|
-
|
|
9
|
-
_LOGGER = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ProxyConfig(BaseModel):
|
|
13
|
-
verify: bool | str
|
|
14
|
-
proxy: str | None
|
|
15
|
-
headers: dict[str, str] | None
|
|
16
|
-
cert: tuple[str, str] | str | None = None
|
|
17
|
-
trust_env: bool = False
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _get_proxy_host_and_port() -> tuple[str, int]:
|
|
21
|
-
proxy_host = env_settings.proxy_host
|
|
22
|
-
proxy_port = env_settings.proxy_port
|
|
23
|
-
assert proxy_host is not None and proxy_port is not None, (
|
|
24
|
-
"Proxy host and port are required"
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
return proxy_host, proxy_port
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _build_proxy_url_with_username_password() -> str:
|
|
31
|
-
proxy_host, proxy_port = _get_proxy_host_and_port()
|
|
32
|
-
|
|
33
|
-
proxy_username = env_settings.proxy_username
|
|
34
|
-
proxy_password = env_settings.proxy_password
|
|
35
|
-
assert proxy_username is not None and proxy_password is not None, (
|
|
36
|
-
"Proxy username and password are required"
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
return f"{env_settings.proxy_protocol}://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _build_proxy_url_with_tls() -> str:
|
|
43
|
-
proxy_host, proxy_port = _get_proxy_host_and_port()
|
|
44
|
-
return f"{env_settings.proxy_protocol}://{proxy_host}:{proxy_port}"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _get_cert_args() -> tuple[str, str] | str:
|
|
48
|
-
proxy_ssl_cert_path = env_settings.proxy_ssl_cert_path
|
|
49
|
-
assert proxy_ssl_cert_path is not None, "Proxy SSL cert path is required"
|
|
50
|
-
|
|
51
|
-
proxy_ssl_key_path = env_settings.proxy_ssl_key_path
|
|
52
|
-
|
|
53
|
-
if proxy_ssl_key_path is None:
|
|
54
|
-
_LOGGER.warning(
|
|
55
|
-
"Proxy SSL key path is not set. Assuming cert path includes key path"
|
|
56
|
-
)
|
|
57
|
-
return proxy_ssl_cert_path
|
|
58
|
-
else:
|
|
59
|
-
return proxy_ssl_cert_path, proxy_ssl_key_path
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def _get_none_proxy_kwargs() -> ProxyConfig:
|
|
63
|
-
_LOGGER.info("Proxy auth mode: none. Using no proxy")
|
|
64
|
-
return ProxyConfig(
|
|
65
|
-
proxy=None,
|
|
66
|
-
headers=None,
|
|
67
|
-
verify=True,
|
|
68
|
-
trust_env=True,
|
|
69
|
-
cert=None,
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def _get_username_password_proxy_kwargs() -> ProxyConfig:
|
|
74
|
-
proxy_url = _build_proxy_url_with_username_password()
|
|
75
|
-
_LOGGER.info(
|
|
76
|
-
"Proxy auth mode: username_password. Using proxy with username and password"
|
|
77
|
-
)
|
|
78
|
-
return ProxyConfig(
|
|
79
|
-
proxy=proxy_url,
|
|
80
|
-
headers=env_settings.proxy_headers,
|
|
81
|
-
verify=env_settings.proxy_ssl_ca_bundle_path or True,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _get_ssl_tls_proxy_kwargs() -> ProxyConfig:
|
|
86
|
-
proxy_url = _build_proxy_url_with_tls()
|
|
87
|
-
cert_args = _get_cert_args()
|
|
88
|
-
_LOGGER.info("Proxy auth mode: ssl_tls. Using proxy with SSL/TLS")
|
|
89
|
-
return ProxyConfig(
|
|
90
|
-
proxy=proxy_url,
|
|
91
|
-
cert=cert_args,
|
|
92
|
-
headers=env_settings.proxy_headers,
|
|
93
|
-
verify=env_settings.proxy_ssl_ca_bundle_path or True,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def _build_client_kwargs() -> ProxyConfig:
|
|
98
|
-
match env_settings.proxy_auth_mode:
|
|
99
|
-
case "none":
|
|
100
|
-
return _get_none_proxy_kwargs()
|
|
101
|
-
case "username_password":
|
|
102
|
-
return _get_username_password_proxy_kwargs()
|
|
103
|
-
case "ssl_tls":
|
|
104
|
-
return _get_ssl_tls_proxy_kwargs()
|
|
105
|
-
case _:
|
|
106
|
-
raise ValueError(f"Invalid proxy auth mode: {env_settings.proxy_auth_mode}")
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
_client_kwargs = _build_client_kwargs()
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
async_client = partial(
|
|
113
|
-
AsyncClient,
|
|
114
|
-
proxy=_client_kwargs.proxy,
|
|
115
|
-
headers=_client_kwargs.headers,
|
|
116
|
-
verify=_client_kwargs.verify,
|
|
117
|
-
trust_env=_client_kwargs.trust_env,
|
|
118
|
-
cert=_client_kwargs.cert,
|
|
119
|
-
)
|
|
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
|
|
File without changes
|
{unique_web_search-2026.38.0.dev2 → unique_web_search-2026.38.0.dev4}/src/unique_web_search/utils.py
RENAMED
|
File without changes
|