unique-web-search 2026.36.0.dev2__tar.gz → 2026.36.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.
Files changed (127) hide show
  1. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/PKG-INFO +3 -3
  2. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/pyproject.toml +3 -3
  3. unique_web_search-2026.36.0.dev4/src/unique_web_search/invocation_stats.py +31 -0
  4. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/service.py +7 -7
  5. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/llm_guard_judge.py +2 -2
  6. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/llm_keyword_redact.py +2 -2
  7. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/llm_process.py +2 -2
  8. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/base_executor.py +2 -2
  9. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/executor.py +2 -2
  10. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/README.md +6 -0
  11. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/runner.py +2 -2
  12. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/response_parsing.py +2 -2
  13. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/snippet_judge/service.py +2 -2
  14. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/structured_llm.py +2 -2
  15. unique_web_search-2026.36.0.dev2/src/unique_web_search/invocation_stats.py +0 -113
  16. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/README.md +0 -0
  17. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/__init__.py +0 -0
  18. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/client_settings.py +0 -0
  19. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/config.py +0 -0
  20. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/metrics.py +0 -0
  21. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/prompts.py +0 -0
  22. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/schema.py +0 -0
  23. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/_registry.py +0 -0
  24. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/__init__.py +0 -0
  25. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/config.py +0 -0
  26. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/exceptions.py +0 -0
  27. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/prompts/__init__.py +0 -0
  28. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/prompts/guidelines.j2 +0 -0
  29. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/prompts/guidelines_with_keywords.j2 +0 -0
  30. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/prompts/rejection_response.j2 +0 -0
  31. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/prompts/system_prompt.j2 +0 -0
  32. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/prompts/user_prompt.j2 +0 -0
  33. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/argument_screening/service.py +0 -0
  34. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/client/proxy_config.py +0 -0
  35. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/__init__.py +0 -0
  36. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/cleaning/__init__.py +0 -0
  37. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/cleaning/character_sanitize.py +0 -0
  38. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/cleaning/clean.py +0 -0
  39. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/cleaning/config.py +0 -0
  40. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/config.py +0 -0
  41. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/base.py +0 -0
  42. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/config.py +0 -0
  43. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/__init__.py +0 -0
  44. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_and_sanitize_prompt.j2 +0 -0
  45. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_prompt.j2 +0 -0
  46. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/keyword_extract_prompt.j2 +0 -0
  47. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/page_context.j2 +0 -0
  48. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/sanitize_rules.j2 +0 -0
  49. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/system_prompt.j2 +0 -0
  50. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/prompts/user_prompt.j2 +0 -0
  51. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/schema.py +0 -0
  52. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/settings.py +0 -0
  53. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/processing_strategies/truncate.py +0 -0
  54. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/content_processing/service.py +0 -0
  55. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/README.md +0 -0
  56. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/__init__.py +0 -0
  57. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/base.py +0 -0
  58. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/basic.py +0 -0
  59. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/firecrawl.py +0 -0
  60. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/jina.py +0 -0
  61. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/registry.py +0 -0
  62. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/tavily.py +0 -0
  63. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/url_safety/__init__.py +0 -0
  64. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/crawlers/utils.py +0 -0
  65. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/README.md +0 -0
  66. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/__init__.py +0 -0
  67. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/base_config.py +0 -0
  68. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/context.py +0 -0
  69. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/modes.py +0 -0
  70. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/__init__.py +0 -0
  71. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/config.py +0 -0
  72. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/prompts/__init__.py +0 -0
  73. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/prompts/refine_query_system_prompt.j2 +0 -0
  74. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/prompts/tool_description.j2 +0 -0
  75. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/prompts/tool_description_for_system_prompt.j2 +0 -0
  76. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/schema.py +0 -0
  77. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v1/strategy.py +0 -0
  78. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/__init__.py +0 -0
  79. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/config.py +0 -0
  80. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/executor.py +0 -0
  81. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/prompts/__init__.py +0 -0
  82. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/prompts/step_query_description.j2 +0 -0
  83. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/prompts/tool_description.j2 +0 -0
  84. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/prompts/tool_description_for_system_prompt.j2 +0 -0
  85. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/schema.py +0 -0
  86. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v2/strategy.py +0 -0
  87. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/__init__.py +0 -0
  88. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/config.py +0 -0
  89. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/executor.py +0 -0
  90. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/prompts/__init__.py +0 -0
  91. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/prompts/tool_description.j2 +0 -0
  92. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/prompts/tool_description_for_system_prompt.j2 +0 -0
  93. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/schema.py +0 -0
  94. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/executors/v3/strategy.py +0 -0
  95. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/helpers.py +0 -0
  96. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/message_log.py +0 -0
  97. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/proxy/__init__.py +0 -0
  98. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/proxy/bridge.py +0 -0
  99. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/proxy/mappers.py +0 -0
  100. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/query_elicitation.py +0 -0
  101. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/__init__.py +0 -0
  102. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/base.py +0 -0
  103. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/bing.py +0 -0
  104. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/brave.py +0 -0
  105. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/custom_api.py +0 -0
  106. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/google.py +0 -0
  107. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/perplexity.py +0 -0
  108. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/registry.py +0 -0
  109. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/schema.py +0 -0
  110. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/google/schema.py +0 -0
  111. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/__init__.py +0 -0
  112. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/__init__.py +0 -0
  113. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/client.py +0 -0
  114. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/bing/models.py +0 -0
  115. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/models.py +0 -0
  116. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/__init__.py +0 -0
  117. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/client.py +0 -0
  118. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/exceptions.py +0 -0
  119. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/gemini.py +0 -0
  120. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/response_handler.py +0 -0
  121. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/search_engine/vertexai.py +0 -0
  122. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/snippet_judge/__init__.py +0 -0
  123. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/snippet_judge/prompts.py +0 -0
  124. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/services/snippet_judge/schema.py +0 -0
  125. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/settings.py +0 -0
  126. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.0.dev4}/src/unique_web_search/skills/web-search-v3.md +0 -0
  127. {unique_web_search-2026.36.0.dev2 → unique_web_search-2026.36.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.36.0.dev2
3
+ Version: 2026.36.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.36.0.dev6,<2026.36.0rc0
19
+ Requires-Dist: unique-toolkit[monitoring]>=2026.36.0.dev10,<2026.36.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.36.0.dev2,<2026.36.0rc0
28
+ Requires-Dist: unique-search-proxy-core>=2026.36.0.dev3,<2026.36.0rc0
29
29
  Requires-Dist: unique-search-proxy-sdk>=2026.36.0.dev1,<2026.36.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.36.0.dev2"
3
+ version = "2026.36.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.36.0.dev6,<2026.36.0rc0",
25
+ "unique-toolkit[monitoring]>=2026.36.0.dev10,<2026.36.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.36.0.dev2,<2026.36.0rc0",
34
+ "unique-search-proxy-core>=2026.36.0.dev3,<2026.36.0rc0",
35
35
  "unique-search-proxy-sdk>=2026.36.0.dev1,<2026.36.0rc0",
36
36
  "openai>=1.105.0,<3",
37
37
  ]
@@ -0,0 +1,31 @@
1
+ """Run-scoped collection of language-model invocation usage."""
2
+
3
+ from typing import Any
4
+
5
+ from unique_toolkit.language_model.invocation_stats import InvocationStatsCollector
6
+
7
+ collector = InvocationStatsCollector("web_search")
8
+
9
+
10
+ def record_vertex_response(
11
+ *,
12
+ model_name: str,
13
+ response: Any,
14
+ source: str,
15
+ ) -> None:
16
+ """Record Google GenAI usage metadata using toolkit token names."""
17
+ usage = getattr(response, "usage_metadata", None)
18
+ if usage is None:
19
+ return
20
+
21
+ collector.record_token_usage(
22
+ model_name=model_name,
23
+ usage={
24
+ "prompt_tokens": getattr(usage, "prompt_token_count", None),
25
+ "completion_tokens": getattr(usage, "candidates_token_count", None),
26
+ "total_tokens": getattr(usage, "total_token_count", None),
27
+ "reasoning_tokens": getattr(usage, "thoughts_token_count", None),
28
+ "cached_tokens": getattr(usage, "cached_content_token_count", None),
29
+ },
30
+ source=source,
31
+ )
@@ -8,14 +8,13 @@ from unique_search_proxy_core.param_policy.exposed_params import ExposedParams
8
8
  from unique_search_proxy_core.search_engines.base import BaseSearchEngineConfig
9
9
  from unique_toolkit._common.chunk_relevancy_sorter.service import ChunkRelevancySorter
10
10
  from unique_toolkit.agentic.evaluation.schemas import EvaluationMetricName
11
- from unique_toolkit.agentic.feature_flags.feature_flags import (
12
- feature_flags,
13
- )
11
+ from unique_toolkit.agentic.feature_flags import FeatureFlagNames
14
12
  from unique_toolkit.agentic.tools.factory import ToolFactory
15
13
  from unique_toolkit.agentic.tools.schemas import ToolCallResponse
16
14
  from unique_toolkit.agentic.tools.tool import (
17
15
  Tool,
18
16
  )
17
+ from unique_toolkit.experimental.resources.feature_flags import is_flag_enabled
19
18
  from unique_toolkit.language_model.infos import LanguageModelInfo
20
19
  from unique_toolkit.language_model.schemas import (
21
20
  LanguageModelFunction,
@@ -24,7 +23,7 @@ from unique_toolkit.language_model.schemas import (
24
23
  from unique_toolkit.monitoring import metric_scope
25
24
 
26
25
  from unique_web_search.config import WebSearchConfig
27
- from unique_web_search.invocation_stats import invocation_stats_scope
26
+ from unique_web_search.invocation_stats import collector
28
27
  from unique_web_search.metrics import tool_duration, tool_empty_results, tool_errors
29
28
  from unique_web_search.schema import WebSearchDebugInfo
30
29
  from unique_web_search.services.argument_screening import (
@@ -152,7 +151,7 @@ class WebSearchTool(Tool[WebSearchConfig]):
152
151
 
153
152
  @override
154
153
  async def run(self, tool_call: LanguageModelFunction) -> ToolCallResponse:
155
- with invocation_stats_scope() as invocation_stats:
154
+ with collector.scope() as invocation_stats:
156
155
  try:
157
156
  response = await self._run(tool_call)
158
157
  except Exception as e:
@@ -311,8 +310,9 @@ class WebSearchTool(Tool[WebSearchConfig]):
311
310
  async def _get_argument_screening_service_if_ff_enabled(
312
311
  self,
313
312
  ) -> ArgumentScreeningService | None:
314
- if not feature_flags.enable_web_search_argument_screening_un_18741.is_enabled(
315
- self.company_id
313
+ if not await is_flag_enabled(
314
+ FeatureFlagNames.enable_web_search_argument_screening_un_18741,
315
+ company_id=self.company_id,
316
316
  ):
317
317
  return None
318
318
 
@@ -6,7 +6,7 @@ from unique_toolkit._common.utils.jinja.render import render_template
6
6
  from unique_toolkit.language_model import LanguageModelService
7
7
  from unique_toolkit.language_model.builder import MessagesBuilder
8
8
 
9
- from unique_web_search.invocation_stats import record_language_model_response
9
+ from unique_web_search.invocation_stats import collector
10
10
  from unique_web_search.services.content_processing.processing_strategies.base import (
11
11
  ProcessingStrategyKwargs,
12
12
  WebSearchResult,
@@ -317,7 +317,7 @@ class LLMGuardJudge:
317
317
  "web_search.content_processing.guard_judge_and_sanitize"
318
318
  ),
319
319
  }
320
- record_language_model_response(
320
+ collector.record_language_model_response(
321
321
  model_name=self._config.language_model.name,
322
322
  response=response,
323
323
  source=source_by_response_model[response_model],
@@ -8,7 +8,7 @@ from unique_toolkit._common.utils.jinja.render import render_template
8
8
  from unique_toolkit.language_model import LanguageModelService
9
9
  from unique_toolkit.language_model.builder import MessagesBuilder
10
10
 
11
- from unique_web_search.invocation_stats import record_language_model_response
11
+ from unique_web_search.invocation_stats import collector
12
12
  from unique_web_search.services.content_processing.processing_strategies.base import (
13
13
  ProcessingStrategyKwargs,
14
14
  WebSearchResult,
@@ -181,7 +181,7 @@ class LLMKeywordRedact:
181
181
  structured_output_model=KeywordRedactResponse,
182
182
  structured_output_enforce_schema=True,
183
183
  )
184
- record_language_model_response(
184
+ collector.record_language_model_response(
185
185
  model_name=self._config.language_model.name,
186
186
  response=response,
187
187
  source="web_search.content_processing.keyword_redact",
@@ -13,7 +13,7 @@ from unique_toolkit.language_model import (
13
13
  )
14
14
  from unique_toolkit.language_model.builder import MessagesBuilder
15
15
 
16
- from unique_web_search.invocation_stats import record_language_model_response
16
+ from unique_web_search.invocation_stats import collector
17
17
  from unique_web_search.services.content_processing.processing_strategies.base import (
18
18
  ProcessingStrategyKwargs,
19
19
  WebSearchResult,
@@ -317,7 +317,7 @@ class LLMProcess:
317
317
  structured_output_model=LLMProcessorResponse,
318
318
  structured_output_enforce_schema=True,
319
319
  )
320
- record_language_model_response(
320
+ collector.record_language_model_response(
321
321
  model_name=self._config.language_model.name,
322
322
  response=response,
323
323
  source="web_search.content_processing.llm_process",
@@ -12,7 +12,7 @@ from unique_toolkit.content import ContentChunk
12
12
  from unique_toolkit.language_model import LanguageModelFunction
13
13
  from unique_toolkit.monitoring import metric_scope
14
14
 
15
- from unique_web_search.invocation_stats import record_invocation_stats
15
+ from unique_web_search.invocation_stats import collector
16
16
  from unique_web_search.metrics import llm_duration, llm_errors
17
17
  from unique_web_search.schema import (
18
18
  StepDebugInfo,
@@ -165,7 +165,7 @@ class BaseWebSearchExecutor(ABC, Generic[T]):
165
165
  # usage to each ChunkRelevancy.relevancy.invocation_stats; merge it
166
166
  # into the active WebSearch stats scope so it isn't dropped from
167
167
  # token analytics (Internal Search does the same after its sort).
168
- record_invocation_stats(
168
+ collector.record_invocation_stats(
169
169
  invocation
170
170
  for relevancy in sorted_chunks.relevancies
171
171
  if relevancy.relevancy is not None
@@ -12,7 +12,7 @@ from unique_toolkit.language_model import LanguageModelFunction
12
12
  from unique_toolkit.language_model.builder import MessagesBuilder
13
13
  from unique_toolkit.monitoring import metric_scope
14
14
 
15
- from unique_web_search.invocation_stats import record_language_model_response
15
+ from unique_web_search.invocation_stats import collector
16
16
  from unique_web_search.metrics import (
17
17
  crawl_duration,
18
18
  crawl_errors,
@@ -125,7 +125,7 @@ async def query_generation_agent(
125
125
  structured_output_model=structured_output_model,
126
126
  structured_output_enforce_schema=True,
127
127
  )
128
- record_language_model_response(
128
+ collector.record_language_model_response(
129
129
  model_name=language_model.name,
130
130
  response=response,
131
131
  source="web_search.query_refinement",
@@ -161,6 +161,12 @@ AZURE_IDENTITY_CREDENTIAL_TYPE=workload # or default
161
161
 
162
162
  Requires GCP ADC / workload identity. Fields include `vertexai_model_name`, `generation_instructions`, `enable_entreprise_search`, `enable_redirect_resolution`, `requires_scraping`.
163
163
 
164
+ ```bash
165
+ VERTEXAI_AGENT_DEFAULT_VERTEX_AI_MODEL_NAME=gemini-3-flash-preview
166
+ # ZDR tenants: pin "Enable Enterprise Search" on and disable the checkbox
167
+ VERTEXAI_AGENT_FORCE_ACTIVATE_ENTERPRISE_SEARCH=false
168
+ ```
169
+
164
170
  ---
165
171
 
166
172
  ## Factory
@@ -24,7 +24,7 @@ from openai.types.responses.response_output_text import (
24
24
  )
25
25
  from openai.types.responses.response_text_delta_event import ResponseTextDeltaEvent
26
26
 
27
- from unique_web_search.invocation_stats import record_token_usage
27
+ from unique_web_search.invocation_stats import collector
28
28
  from unique_web_search.services.search_engine.schema import (
29
29
  WebSearchResult,
30
30
  )
@@ -284,7 +284,7 @@ async def _run_responses_agent(
284
284
  if output_text and not emitted_text:
285
285
  emitted_text = True
286
286
  answer_parts.append(output_text)
287
- record_token_usage(
287
+ collector.record_token_usage(
288
288
  model_name=model,
289
289
  usage=event.response.usage,
290
290
  source="web_search.grounding.bing",
@@ -6,7 +6,7 @@ from unique_toolkit._common.validators import LMI
6
6
  from unique_toolkit.language_model.builder import MessagesBuilder
7
7
  from unique_toolkit.language_model.service import LanguageModelService
8
8
 
9
- from unique_web_search.invocation_stats import record_language_model_response
9
+ from unique_web_search.invocation_stats import collector
10
10
  from unique_web_search.services.search_engine.schema import WebSearchResult
11
11
  from unique_web_search.services.search_engine.utils.grounding.models import (
12
12
  GroundingSearchResults,
@@ -93,7 +93,7 @@ class LLMParserStrategy(ResponseParser):
93
93
  structured_output_model=GroundingSearchResults,
94
94
  structured_output_enforce_schema=True,
95
95
  )
96
- record_language_model_response(
96
+ collector.record_language_model_response(
97
97
  model_name=self.llm.name,
98
98
  response=llm_response,
99
99
  source="web_search.grounding.response_parser",
@@ -9,7 +9,7 @@ from unique_toolkit._common.validators import LMI
9
9
  from unique_toolkit.language_model import LanguageModelService
10
10
  from unique_toolkit.language_model.builder import MessagesBuilder
11
11
 
12
- from unique_web_search.invocation_stats import record_language_model_response
12
+ from unique_web_search.invocation_stats import collector
13
13
  from unique_web_search.services.helpers import extract_registered_domain
14
14
  from unique_web_search.services.search_engine.schema import (
15
15
  WebSearchResult,
@@ -100,7 +100,7 @@ async def score_and_explain(
100
100
  structured_output_model=SnippetJudgeResponse,
101
101
  structured_output_enforce_schema=True,
102
102
  )
103
- record_language_model_response(
103
+ collector.record_language_model_response(
104
104
  model_name=language_model.name,
105
105
  response=response,
106
106
  source="web_search.snippet_judge",
@@ -7,7 +7,7 @@ from unique_toolkit._common.validators import LMI
7
7
  from unique_toolkit.language_model import LanguageModelService
8
8
  from unique_toolkit.language_model.builder import MessagesBuilder
9
9
 
10
- from unique_web_search.invocation_stats import record_language_model_response
10
+ from unique_web_search.invocation_stats import collector
11
11
 
12
12
  T = TypeVar("T", bound=BaseModel)
13
13
 
@@ -40,7 +40,7 @@ async def complete_structured_llm(
40
40
  structured_output_model=response_model,
41
41
  structured_output_enforce_schema=structured_output_enforce_schema,
42
42
  )
43
- record_language_model_response(
43
+ collector.record_language_model_response(
44
44
  model_name=language_model.name,
45
45
  response=response,
46
46
  source=invocation_source,
@@ -1,113 +0,0 @@
1
- """Run-scoped collection of language-model invocation usage."""
2
-
3
- import logging
4
- from collections.abc import Iterable, Iterator
5
- from contextlib import contextmanager
6
- from contextvars import ContextVar, Token
7
- from typing import Any
8
-
9
- from pydantic import ValidationError
10
- from unique_toolkit.language_model.invocation_stats import LanguageModelInvocationStats
11
- from unique_toolkit.language_model.schemas import LanguageModelTokenUsage
12
-
13
- _LOGGER = logging.getLogger(__name__)
14
-
15
- _CURRENT_INVOCATION_STATS: ContextVar[list[LanguageModelInvocationStats] | None] = (
16
- ContextVar("web_search_invocation_stats", default=None)
17
- )
18
-
19
-
20
- @contextmanager
21
- def invocation_stats_scope() -> Iterator[list[LanguageModelInvocationStats]]:
22
- """Collect stats for one WebSearch run and isolate concurrent runs."""
23
- invocation_stats: list[LanguageModelInvocationStats] = []
24
- token: Token[list[LanguageModelInvocationStats] | None] = (
25
- _CURRENT_INVOCATION_STATS.set(invocation_stats)
26
- )
27
- try:
28
- yield invocation_stats
29
- finally:
30
- _CURRENT_INVOCATION_STATS.reset(token)
31
-
32
-
33
- def record_invocation_stats(
34
- invocation_stats: Iterable[LanguageModelInvocationStats],
35
- ) -> None:
36
- """Record already-built invocation stats when a run scope is active.
37
-
38
- Used for usage produced by nested dependencies that hand back
39
- ``LanguageModelInvocationStats`` objects directly (e.g. the chunk relevancy
40
- sorter attaching per-chunk stats on its result), rather than a raw provider
41
- usage payload.
42
- """
43
- current_invocation_stats = _CURRENT_INVOCATION_STATS.get()
44
- if current_invocation_stats is not None:
45
- current_invocation_stats.extend(invocation_stats)
46
-
47
-
48
- def record_token_usage(
49
- *,
50
- model_name: str,
51
- usage: Any,
52
- source: str,
53
- ) -> None:
54
- """Record provider usage when both a run scope and usage are available."""
55
- invocation_stats = _CURRENT_INVOCATION_STATS.get()
56
- if invocation_stats is None or usage is None:
57
- return
58
-
59
- try:
60
- token_usage = LanguageModelTokenUsage.model_validate(usage)
61
- except ValidationError:
62
- _LOGGER.warning(
63
- "Unable to parse Web Search token usage for %s",
64
- source,
65
- exc_info=True,
66
- )
67
- return
68
-
69
- invocation_stats.append(
70
- LanguageModelInvocationStats.from_usage(
71
- model_name=model_name,
72
- token_usage=token_usage,
73
- source=source,
74
- )
75
- )
76
-
77
-
78
- def record_language_model_response(
79
- *,
80
- model_name: str,
81
- response: Any,
82
- source: str,
83
- ) -> None:
84
- """Record usage from a Unique Toolkit language-model response."""
85
- record_token_usage(
86
- model_name=model_name,
87
- usage=getattr(response, "usage", None),
88
- source=source,
89
- )
90
-
91
-
92
- def record_vertex_response(
93
- *,
94
- model_name: str,
95
- response: Any,
96
- source: str,
97
- ) -> None:
98
- """Record Google GenAI usage metadata using toolkit token names."""
99
- usage = getattr(response, "usage_metadata", None)
100
- if usage is None:
101
- return
102
-
103
- record_token_usage(
104
- model_name=model_name,
105
- usage={
106
- "prompt_tokens": getattr(usage, "prompt_token_count", None),
107
- "completion_tokens": getattr(usage, "candidates_token_count", None),
108
- "total_tokens": getattr(usage, "total_token_count", None),
109
- "reasoning_tokens": getattr(usage, "thoughts_token_count", None),
110
- "cached_tokens": getattr(usage, "cached_content_token_count", None),
111
- },
112
- source=source,
113
- )