unique-web-search 2026.26.0.dev0__tar.gz → 2026.26.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.
Files changed (128) hide show
  1. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/PKG-INFO +4 -2
  2. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/pyproject.toml +6 -2
  3. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/client_settings.py +27 -0
  4. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/__init__.py +16 -1
  5. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/base.py +1 -1
  6. unique_web_search-2026.26.0.dev2/src/unique_web_search/services/crawlers/basic_proxy.py +79 -0
  7. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/settings.py +4 -1
  8. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/README.md +0 -0
  9. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/__init__.py +0 -0
  10. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/config.py +0 -0
  11. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/metrics.py +0 -0
  12. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/prompts.py +0 -0
  13. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/schema.py +0 -0
  14. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/service.py +0 -0
  15. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/__init__.py +0 -0
  16. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/config.py +0 -0
  17. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/exceptions.py +0 -0
  18. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/prompts/__init__.py +0 -0
  19. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/prompts/guidelines.j2 +0 -0
  20. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/prompts/rejection_response.j2 +0 -0
  21. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/prompts/system_prompt.j2 +0 -0
  22. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/prompts/user_prompt.j2 +0 -0
  23. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/argument_screening/service.py +0 -0
  24. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/client/proxy_config.py +0 -0
  25. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/__init__.py +0 -0
  26. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/cleaning/__init__.py +0 -0
  27. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/cleaning/character_sanitize.py +0 -0
  28. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/cleaning/clean.py +0 -0
  29. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/cleaning/config.py +0 -0
  30. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/config.py +0 -0
  31. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/base.py +0 -0
  32. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/config.py +0 -0
  33. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/llm_guard_judge.py +0 -0
  34. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/llm_keyword_redact.py +0 -0
  35. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/llm_process.py +0 -0
  36. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/__init__.py +0 -0
  37. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_and_sanitize_prompt.j2 +0 -0
  38. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_prompt.j2 +0 -0
  39. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/keyword_extract_prompt.j2 +0 -0
  40. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/page_context.j2 +0 -0
  41. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/sanitize_rules.j2 +0 -0
  42. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/system_prompt.j2 +0 -0
  43. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/prompts/user_prompt.j2 +0 -0
  44. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/schema.py +0 -0
  45. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/settings.py +0 -0
  46. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/processing_strategies/truncate.py +0 -0
  47. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/content_processing/service.py +0 -0
  48. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/README.md +0 -0
  49. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/basic.py +0 -0
  50. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/crawl4ai.py +0 -0
  51. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/firecrawl.py +0 -0
  52. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/jina.py +0 -0
  53. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/tavily.py +0 -0
  54. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/__init__.py +0 -0
  55. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/dns.py +0 -0
  56. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/models.py +0 -0
  57. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/netloc.py +0 -0
  58. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/policy.py +0 -0
  59. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/redirect.py +0 -0
  60. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/resolver.py +0 -0
  61. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/service.py +0 -0
  62. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/url_safety/settings.py +0 -0
  63. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/crawlers/utils.py +0 -0
  64. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/README.md +0 -0
  65. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/__init__.py +0 -0
  66. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/base_config.py +0 -0
  67. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/base_executor.py +0 -0
  68. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/context.py +0 -0
  69. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/__init__.py +0 -0
  70. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/config.py +0 -0
  71. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/executor.py +0 -0
  72. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/prompts/__init__.py +0 -0
  73. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/prompts/refine_query_system_prompt.j2 +0 -0
  74. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/prompts/restrict_date_description.j2 +0 -0
  75. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/prompts/tool_description.j2 +0 -0
  76. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/prompts/tool_description_for_system_prompt.j2 +0 -0
  77. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v1/schema.py +0 -0
  78. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/__init__.py +0 -0
  79. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/config.py +0 -0
  80. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/executor.py +0 -0
  81. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/prompts/__init__.py +0 -0
  82. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/prompts/step_query_description.j2 +0 -0
  83. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/prompts/tool_description.j2 +0 -0
  84. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/prompts/tool_description_for_system_prompt.j2 +0 -0
  85. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v2/schema.py +0 -0
  86. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/__init__.py +0 -0
  87. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/config.py +0 -0
  88. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/executor.py +0 -0
  89. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/prompts/__init__.py +0 -0
  90. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/prompts/tool_description.j2 +0 -0
  91. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/prompts/tool_description_for_system_prompt.j2 +0 -0
  92. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/executors/v3/schema.py +0 -0
  93. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/helpers.py +0 -0
  94. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/message_log.py +0 -0
  95. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/query_elicitation.py +0 -0
  96. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/README.md +0 -0
  97. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/__init__.py +0 -0
  98. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/base.py +0 -0
  99. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/bing.py +0 -0
  100. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/brave.py +0 -0
  101. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/custom_api.py +0 -0
  102. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/firecrawl.py +0 -0
  103. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/google.py +0 -0
  104. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/jina.py +0 -0
  105. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/perplexity.py +0 -0
  106. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/schema.py +0 -0
  107. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/tavily.py +0 -0
  108. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/google/schema.py +0 -0
  109. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/__init__.py +0 -0
  110. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/__init__.py +0 -0
  111. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/client.py +0 -0
  112. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/models.py +0 -0
  113. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/bing/runner.py +0 -0
  114. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/models.py +0 -0
  115. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/response_parsing.py +0 -0
  116. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/__init__.py +0 -0
  117. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/client.py +0 -0
  118. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/exceptions.py +0 -0
  119. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/gemini.py +0 -0
  120. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/response_handler.py +0 -0
  121. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/search_engine/vertexai.py +0 -0
  122. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/snippet_judge/__init__.py +0 -0
  123. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/snippet_judge/prompts.py +0 -0
  124. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/snippet_judge/schema.py +0 -0
  125. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/snippet_judge/service.py +0 -0
  126. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/services/structured_llm.py +0 -0
  127. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.0.dev2}/src/unique_web_search/skills/web-search-v3.md +0 -0
  128. {unique_web_search-2026.26.0.dev0 → unique_web_search-2026.26.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.26.0.dev0
3
+ Version: 2026.26.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>
@@ -17,7 +17,7 @@ Requires-Dist: tavily-python>=0.7.11,<1
17
17
  Requires-Dist: unidecode>=1.4.0,<2
18
18
  Requires-Dist: azure-ai-projects>=1.0.0,<2
19
19
  Requires-Dist: azure-identity>=1.25.0,<2
20
- Requires-Dist: unique-toolkit[monitoring]>=2026.26.0.dev0,<2026.26.0rc0
20
+ Requires-Dist: unique-toolkit[monitoring]>=2026.26.0.dev2,<2026.26.0rc0
21
21
  Requires-Dist: azure-core>=1.36.0,<2
22
22
  Requires-Dist: google-cloud-aiplatform>=1.128.0,<2
23
23
  Requires-Dist: google-auth>=2.43.0,<3
@@ -28,6 +28,8 @@ Requires-Dist: certifi>=2025.11.12
28
28
  Requires-Dist: jinja2>=3.1.6
29
29
  Requires-Dist: playwright>=1.58.0
30
30
  Requires-Dist: perplexityai>=0.34.0
31
+ Requires-Dist: unique-search-proxy-core>=2026.26.0.dev1,<2026.26.0rc0
32
+ Requires-Dist: unique-search-proxy-sdk>=2026.26.0.dev1,<2026.26.0rc0
31
33
  Requires-Python: >=3.12, <4.0
32
34
  Description-Content-Type: text/markdown
33
35
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_web_search"
3
- version = "2026.26.0.dev0"
3
+ version = "2026.26.0.dev2"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12,<4.0"
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "unidecode>=1.4.0,<2",
24
24
  "azure-ai-projects>=1.0.0,<2",
25
25
  "azure-identity>=1.25.0,<2",
26
- "unique-toolkit[monitoring]>=2026.26.0.dev0,<2026.26.0rc0",
26
+ "unique-toolkit[monitoring]>=2026.26.0.dev2,<2026.26.0rc0",
27
27
  "azure-core>=1.36.0,<2",
28
28
  "google-cloud-aiplatform>=1.128.0,<2",
29
29
  "google-auth>=2.43.0,<3",
@@ -34,6 +34,8 @@ dependencies = [
34
34
  "jinja2>=3.1.6",
35
35
  "playwright>=1.58.0",
36
36
  "perplexityai>=0.34.0",
37
+ "unique-search-proxy-core>=2026.26.0.dev1,<2026.26.0rc0",
38
+ "unique-search-proxy-sdk>=2026.26.0.dev1,<2026.26.0rc0",
37
39
  ]
38
40
 
39
41
  [build-system]
@@ -91,3 +93,5 @@ extend-select = ["I"]
91
93
 
92
94
  [tool.uv.sources]
93
95
  unique-toolkit = { workspace = true }
96
+ unique-search-proxy-sdk = { workspace = true }
97
+ unique-search-proxy-core = { workspace = true }
@@ -235,3 +235,30 @@ def get_perplexity_search_settings() -> PerplexitySearchSettings:
235
235
  if _perplexity_search_settings is None:
236
236
  _perplexity_search_settings = PerplexitySearchSettings.from_env_settings()
237
237
  return _perplexity_search_settings
238
+
239
+
240
+ class SearchProxySettings(BaseModel):
241
+ base_url: str | None = None
242
+
243
+ @property
244
+ def is_configured(self) -> bool:
245
+ return self.base_url is not None
246
+
247
+ @classmethod
248
+ def from_env_settings(cls):
249
+ if env_settings.search_proxy_base_url is None:
250
+ _LOGGER.warning("Unique Search Proxy missing required setting: Base URL")
251
+ else:
252
+ _LOGGER.info("Unique Search Proxy is properly configured")
253
+
254
+ return cls(base_url=env_settings.search_proxy_base_url)
255
+
256
+
257
+ _search_proxy_settings: SearchProxySettings | None = None
258
+
259
+
260
+ def get_search_proxy_settings() -> SearchProxySettings:
261
+ global _search_proxy_settings
262
+ if _search_proxy_settings is None:
263
+ _search_proxy_settings = SearchProxySettings.from_env_settings()
264
+ return _search_proxy_settings
@@ -9,6 +9,10 @@ from unique_web_search.services.crawlers.basic import (
9
9
  BasicCrawler,
10
10
  BasicCrawlerConfig,
11
11
  )
12
+ from unique_web_search.services.crawlers.basic_proxy import (
13
+ BasicCrawlerProxyConfig,
14
+ BasicProxyCrawler,
15
+ )
12
16
  from unique_web_search.services.crawlers.crawl4ai import (
13
17
  Crawl4AiCrawler,
14
18
  Crawl4AiCrawlerConfig,
@@ -27,11 +31,17 @@ from unique_web_search.services.crawlers.tavily import (
27
31
  )
28
32
 
29
33
  CrawlerTypes = (
30
- BasicCrawler | Crawl4AiCrawler | FirecrawlCrawler | JinaCrawler | TavilyCrawler
34
+ BasicCrawler
35
+ | BasicProxyCrawler
36
+ | Crawl4AiCrawler
37
+ | FirecrawlCrawler
38
+ | JinaCrawler
39
+ | TavilyCrawler
31
40
  )
32
41
 
33
42
  CrawlerConfigTypes = (
34
43
  BasicCrawlerConfig
44
+ | BasicCrawlerProxyConfig
35
45
  | Crawl4AiCrawlerConfig
36
46
  | FirecrawlCrawlerConfig
37
47
  | JinaCrawlerConfig
@@ -44,6 +54,7 @@ CRAWLER_NAME_TO_CONFIG = {
44
54
  "firecrawl": FirecrawlCrawlerConfig,
45
55
  "jina": JinaCrawlerConfig,
46
56
  "tavily": TavilyCrawlerConfig,
57
+ "basic_proxy": BasicCrawlerProxyConfig,
47
58
  }
48
59
 
49
60
 
@@ -61,6 +72,8 @@ def get_crawler_service(
61
72
  return FirecrawlCrawler(crawler_config)
62
73
  case CrawlerType.JINA:
63
74
  return JinaCrawler(crawler_config)
75
+ case CrawlerType.BASIC_PROXY:
76
+ return BasicProxyCrawler(crawler_config)
64
77
 
65
78
 
66
79
  def get_crawler_config_types_from_names(crawler_names: list[str]) -> TypeAlias:
@@ -90,6 +103,8 @@ def get_default_crawler_config(
90
103
  __all__ = [
91
104
  "BasicCrawler",
92
105
  "BasicCrawlerConfig",
106
+ "BasicProxyCrawler",
107
+ "BasicCrawlerProxyConfig",
93
108
  "Crawl4AiCrawler",
94
109
  "Crawl4AiCrawlerConfig",
95
110
  "FirecrawlCrawler",
@@ -20,11 +20,11 @@ from unique_web_search.services.helpers import (
20
20
  class CrawlerType(StrEnum):
21
21
  CRAWL4AI = "Crawl4AiCrawler"
22
22
  BASIC = "BasicCrawler"
23
+ BASIC_PROXY = "BasicProxyCrawler"
23
24
  NO_CRAWLER = "NoCrawler"
24
25
  TAVILY = "TavilyCrawler"
25
26
  FIRECRAWL = "FirecrawlCrawler"
26
27
  JINA = "JinaCrawler"
27
- NONE = "None"
28
28
 
29
29
 
30
30
  T = TypeVar("T", bound=CrawlerType)
@@ -0,0 +1,79 @@
1
+ import logging
2
+ from typing import Literal
3
+
4
+ from unique_search_proxy_core.crawlers import (
5
+ BasicCrawlerConfig as BasicCrawlerProxyCoreConfig,
6
+ )
7
+ from unique_search_proxy_core.crawlers.base import CrawlerType as ProxyCrawlerType
8
+ from unique_search_proxy_sdk import UniqueSearchProxyClient
9
+ from unique_search_proxy_sdk._generated.models.crawl_url_result import CrawlUrlResult
10
+ from unique_search_proxy_sdk._generated.models.per_url_error import PerUrlError
11
+
12
+ from unique_web_search.client_settings import get_search_proxy_settings
13
+ from unique_web_search.services.crawlers.base import BaseCrawlerConfig, CrawlerType
14
+
15
+ _LOGGER = logging.getLogger(__name__)
16
+
17
+
18
+ class BasicCrawlerProxyConfig(
19
+ BaseCrawlerConfig[CrawlerType.BASIC_PROXY], BasicCrawlerProxyCoreConfig
20
+ ):
21
+ crawler_type: Literal[CrawlerType.BASIC_PROXY] = CrawlerType.BASIC_PROXY
22
+
23
+
24
+ class BasicProxyCrawler:
25
+ """Crawler that delegates fetching to the remote Unique Search Proxy service.
26
+
27
+ Intermediate transition crawler: instead of fetching URLs in-process (see
28
+ :class:`BasicCrawler`), it forwards the request to the search-proxy service
29
+ via :class:`UniqueSearchProxyClient` and maps the per-URL results back to the
30
+ markdown list contract shared by the other crawlers.
31
+ """
32
+
33
+ def __init__(self, config: BasicCrawlerProxyConfig):
34
+ self.config: BasicCrawlerProxyConfig = config
35
+
36
+ async def crawl(self, urls: list[str]) -> list[str]:
37
+ return await self._crawl(urls)
38
+
39
+ async def _crawl(self, urls: list[str]) -> list[str]:
40
+ base_url = get_search_proxy_settings().base_url
41
+ assert base_url is not None, "Unique Search Proxy base URL is not configured"
42
+
43
+ try:
44
+ async with UniqueSearchProxyClient(
45
+ base_url=base_url,
46
+ timeout=float(self.config.timeout),
47
+ ) as client:
48
+ response = await client.crawl.crawl(
49
+ urls,
50
+ crawler_type=ProxyCrawlerType.BASIC,
51
+ content_types=self.config.content_types,
52
+ timeout=self.config.timeout,
53
+ max_concurrent_requests=self.config.max_concurrent_requests,
54
+ )
55
+ except Exception as error:
56
+ _LOGGER.exception("Unique Search Proxy crawl request failed")
57
+ return [f"Unable to crawl URL via search proxy: {error}" for _ in urls]
58
+
59
+ url_to_markdown = {
60
+ result.url: _result_to_markdown(result) for result in response.results
61
+ }
62
+
63
+ return [
64
+ url_to_markdown.get(url, "Error: URL not found in search proxy response")
65
+ for url in urls
66
+ ]
67
+
68
+
69
+ def _result_to_markdown(result: CrawlUrlResult) -> str:
70
+ if isinstance(result.content, str):
71
+ return result.content
72
+
73
+ if isinstance(result.error, PerUrlError):
74
+ return f"Error: {result.error.message}"
75
+
76
+ if isinstance(result.raw, str):
77
+ return result.raw
78
+
79
+ return "Error: No content returned by search proxy"
@@ -38,7 +38,10 @@ class Base(BaseSettings):
38
38
  active_search_engines: list[str] = ["google"]
39
39
 
40
40
  # Default Crawlers
41
- active_inhouse_crawlers: list[str] = ["basic", "crawl4ai"]
41
+ active_inhouse_crawlers: list[str] = ["basic", "crawl4ai", "basic_proxy"]
42
+
43
+ # Unique Search Proxy service (used by the basic_proxy crawler)
44
+ search_proxy_base_url: str | None = None
42
45
 
43
46
  # Google Search settings
44
47
  google_search_api_key: str | None = None