unique-web-search 1.7.4__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 (57) hide show
  1. unique_web_search-1.7.4/PKG-INFO +147 -0
  2. unique_web_search-1.7.4/README.md +115 -0
  3. unique_web_search-1.7.4/pyproject.toml +59 -0
  4. unique_web_search-1.7.4/src/unique_web_search/__init__.py +0 -0
  5. unique_web_search-1.7.4/src/unique_web_search/client_settings.py +202 -0
  6. unique_web_search-1.7.4/src/unique_web_search/config.py +181 -0
  7. unique_web_search-1.7.4/src/unique_web_search/prompts.py +45 -0
  8. unique_web_search-1.7.4/src/unique_web_search/schema.py +50 -0
  9. unique_web_search-1.7.4/src/unique_web_search/service.py +239 -0
  10. unique_web_search-1.7.4/src/unique_web_search/services/client/proxy_config.py +119 -0
  11. unique_web_search-1.7.4/src/unique_web_search/services/content_processing/__init__.py +15 -0
  12. unique_web_search-1.7.4/src/unique_web_search/services/content_processing/config.py +94 -0
  13. unique_web_search-1.7.4/src/unique_web_search/services/content_processing/service.py +243 -0
  14. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/README.md +349 -0
  15. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/__init__.py +107 -0
  16. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/base.py +54 -0
  17. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/basic.py +119 -0
  18. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/crawl4ai.py +260 -0
  19. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/firecrawl.py +48 -0
  20. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/jina.py +103 -0
  21. unique_web_search-1.7.4/src/unique_web_search/services/crawlers/tavily.py +85 -0
  22. unique_web_search-1.7.4/src/unique_web_search/services/executors/README.md +222 -0
  23. unique_web_search-1.7.4/src/unique_web_search/services/executors/__init__.py +8 -0
  24. unique_web_search-1.7.4/src/unique_web_search/services/executors/base_executor.py +156 -0
  25. unique_web_search-1.7.4/src/unique_web_search/services/executors/configs/__init__.py +32 -0
  26. unique_web_search-1.7.4/src/unique_web_search/services/executors/configs/base.py +26 -0
  27. unique_web_search-1.7.4/src/unique_web_search/services/executors/configs/prompts.py +201 -0
  28. unique_web_search-1.7.4/src/unique_web_search/services/executors/configs/v1_config.py +78 -0
  29. unique_web_search-1.7.4/src/unique_web_search/services/executors/configs/v2_config.py +35 -0
  30. unique_web_search-1.7.4/src/unique_web_search/services/executors/web_search_v1_executor.py +335 -0
  31. unique_web_search-1.7.4/src/unique_web_search/services/executors/web_search_v2_executor.py +288 -0
  32. unique_web_search-1.7.4/src/unique_web_search/services/helpers.py +18 -0
  33. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/README.md +707 -0
  34. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/__init__.py +149 -0
  35. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/base.py +78 -0
  36. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/bing.py +119 -0
  37. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/brave.py +122 -0
  38. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/custom_api.py +130 -0
  39. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/firecrawl.py +142 -0
  40. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/google.py +159 -0
  41. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/jina.py +285 -0
  42. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/schema.py +41 -0
  43. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/tavily.py +151 -0
  44. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/bing/__init__.py +13 -0
  45. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/bing/identity.py +49 -0
  46. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/bing/project.py +21 -0
  47. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/google/schema.py +233 -0
  48. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/__init__.py +31 -0
  49. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/client.py +40 -0
  50. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/config.py +32 -0
  51. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/exceptions.py +25 -0
  52. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/gemini.py +24 -0
  53. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/prompts.py +28 -0
  54. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/utils/vertexai/response_handler.py +87 -0
  55. unique_web_search-1.7.4/src/unique_web_search/services/search_engine/vertexai.py +124 -0
  56. unique_web_search-1.7.4/src/unique_web_search/settings.py +187 -0
  57. unique_web_search-1.7.4/src/unique_web_search/utils.py +128 -0
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.3
2
+ Name: unique-web-search
3
+ Version: 1.7.4
4
+ Summary:
5
+ Author: Andreas Hauri, Gustav Hartz, Rami Azouz
6
+ Author-email: Andreas Hauri <andreas@unique.ch>, Gustav Hartz <gustav.hartz.ext@unique.ch>, Rami Azouz <rami.ext@unique.ch>
7
+ License: Proprietary
8
+ Requires-Dist: typing-extensions>=4.14.1,<5
9
+ Requires-Dist: pydantic>=2.12.3,<3
10
+ Requires-Dist: pydantic-settings>=2.10.1,<3
11
+ Requires-Dist: timeout-decorator>=0.5.0,<1
12
+ Requires-Dist: markdownify>=0.14.1,<1
13
+ Requires-Dist: fake-useragent>=2.2.0,<3
14
+ Requires-Dist: crawl4ai>=0.6.3,<1
15
+ Requires-Dist: firecrawl>=3.3.2,<4
16
+ Requires-Dist: tavily-python>=0.7.11,<1
17
+ Requires-Dist: unidecode>=1.4.0,<2
18
+ Requires-Dist: azure-ai-projects>=1.0.0,<2
19
+ Requires-Dist: azure-identity>=1.25.0,<2
20
+ Requires-Dist: unique-toolkit>=1.38.3,<2
21
+ Requires-Dist: azure-core>=1.36.0,<2
22
+ Requires-Dist: google-cloud-aiplatform>=1.128.0,<2
23
+ Requires-Dist: google-auth>=2.43.0,<3
24
+ Requires-Dist: google-generativeai>=0.8.5,<0.9
25
+ Requires-Dist: langchain-text-splitters>=1.0.0,<2
26
+ Requires-Dist: httpx>=0.28.1
27
+ Requires-Dist: tiktoken>=0.12.0
28
+ Requires-Dist: openai>=1.109.1
29
+ Requires-Dist: certifi>=2025.11.12
30
+ Requires-Python: >=3.12
31
+ Description-Content-Type: text/markdown
32
+
33
+ # Unique Web Search
34
+
35
+ A powerful, configurable web search tool for retrieving and processing the latest information from the internet. This package provides intelligent search capabilities with support for multiple search engines, web crawlers, and content processing strategies.
36
+
37
+ ## Architecture
38
+
39
+ The following diagram illustrates the complete architecture and workflow of the unique_web_search package:
40
+
41
+ ![Web Search Tool Architecture](docs/images/architecture-diagram.svg)
42
+
43
+ ## Key Features
44
+
45
+ - **Dual Execution Modes**:
46
+ - **V1 (Traditional)**: Query refinement with single or multiple search strategies
47
+ - **V2 (Step-based Planning)**: Advanced research planning with parallel execution
48
+
49
+ - **Multiple Search Engines**:
50
+ - Google Search
51
+ - Bing Search
52
+ - Brave Search
53
+ - Jina Search
54
+ - Tavily Search
55
+ - Firecrawl Search
56
+ - VertexAI (Gemini with Grounding)
57
+ - Custom API (integrate any compatible web search API)
58
+
59
+ - **Multiple Web Crawlers**:
60
+ - Basic HTTP Crawler
61
+ - Crawl4AI
62
+ - Jina Reader
63
+ - Tavily Crawler
64
+ - Firecrawl Crawler
65
+
66
+ - **Intelligent Content Processing**:
67
+ - LLM-based summarization
68
+ - Token-based truncation
69
+ - Relevancy scoring and sorting
70
+ - Content chunking and optimization
71
+
72
+ - **Query Refinement**:
73
+ - **BASIC Mode**: Single optimized search query
74
+ - **ADVANCED Mode**: Multiple targeted search queries for complex research
75
+
76
+ - **Performance Optimized**:
77
+ - Parallel execution of search and crawl operations
78
+ - Token limit management
79
+ - Configurable timeouts and error handling
80
+
81
+ ## Detailed Subsystem Docs
82
+
83
+ For deeper dives into each subsystem, see the dedicated READMEs:
84
+
85
+ - [Search Engines](./unique_web_search/services/search_engine/README.md) &mdash; full catalogue of supported engines, configuration, and usage examples.
86
+ - [Crawlers](./unique_web_search/services/crawlers/README.md) &mdash; comparison of crawling strategies (Basic, Crawl4AI, Tavily, Firecrawl, Jina) with setup guides.
87
+ - [Executors](./unique_web_search/services/executors/README.md) &mdash; orchestration layer (V1 & V2) covering query refinement, planning, logging, and best practices.
88
+
89
+ ## Configuration
90
+
91
+ The tool uses environment variables and configuration files to manage API keys and settings. Key configuration areas include:
92
+
93
+ - Search engine selection and API keys
94
+ - Crawler selection and configuration
95
+ - Content processing strategies (SUMMARIZE, TRUNCATE, NONE)
96
+ - Token limits and relevancy thresholds
97
+ - Proxy configuration
98
+ - Debug and monitoring options
99
+
100
+ ## Dependency management (uv.lock + min/latest testing)
101
+
102
+ This package is a **library** and uses `uv` for dependency management.
103
+
104
+ We run tests additionally with minimal dependencies to ensure that the listed ranges are valid. NOTE: We use lowest-direct, not lowest.
105
+ Lowest attempts to use the lowest possible dependency versions _tarnsitively_ causing issues if a dependency has incorrect metadata. Example:
106
+ - google-cloud-aiplatform says it works with shapely<3.0.0.
107
+ - The lowest resolver assumes 1.0 which needs python 2 -> breaks
108
+ Therefore we use lowest-direct which only sets our direct dependencies to lowest. However, this only correctly verifies our min dependencies
109
+ if our code correctly lists all the required dependencies and never imports a transitive dependency. We therefore use deptry to ansure we
110
+ don't use transitive dependencies and that we have no unused dependencies.
111
+
112
+ ### Test locally
113
+
114
+ - **Latest deps and deptry**:
115
+
116
+ ```bash
117
+ cd tool_packages/unique_web_search
118
+ uv sync
119
+ uv run pytest
120
+ uv run deptry
121
+ ```
122
+
123
+
124
+
125
+ - **Min deps**:
126
+
127
+ ```bash
128
+ cd tool_packages/unique_web_search
129
+ uv venv
130
+ # Install runtime deps at minimum versions
131
+ uv pip install -e . --resolution=lowest-direct
132
+ # Install dev deps from [dependency-groups] (we only care about runtime dep minimums)
133
+ uv export --only-group dev --no-hashes | uv pip install -r -
134
+ # Use --no-sync to prevent uv from "fixing" the versions
135
+ uv run --no-sync pytest
136
+ ```
137
+
138
+ ## Workflow
139
+
140
+ 1. **Input**: User query or structured search plan
141
+ 2. **Configuration**: Load settings and initialize services
142
+ 3. **Execution**:
143
+ - V1: Query refinement → Search → Crawl → Process
144
+ - V2: Execute planned steps in parallel → Process
145
+ 4. **Content Processing**: Clean, summarize/truncate, and chunk content
146
+ 5. **Optimization**: Reduce to token limits and sort by relevance
147
+ 6. **Output**: Return structured content chunks optimized for LLM consumption
@@ -0,0 +1,115 @@
1
+ # Unique Web Search
2
+
3
+ A powerful, configurable web search tool for retrieving and processing the latest information from the internet. This package provides intelligent search capabilities with support for multiple search engines, web crawlers, and content processing strategies.
4
+
5
+ ## Architecture
6
+
7
+ The following diagram illustrates the complete architecture and workflow of the unique_web_search package:
8
+
9
+ ![Web Search Tool Architecture](docs/images/architecture-diagram.svg)
10
+
11
+ ## Key Features
12
+
13
+ - **Dual Execution Modes**:
14
+ - **V1 (Traditional)**: Query refinement with single or multiple search strategies
15
+ - **V2 (Step-based Planning)**: Advanced research planning with parallel execution
16
+
17
+ - **Multiple Search Engines**:
18
+ - Google Search
19
+ - Bing Search
20
+ - Brave Search
21
+ - Jina Search
22
+ - Tavily Search
23
+ - Firecrawl Search
24
+ - VertexAI (Gemini with Grounding)
25
+ - Custom API (integrate any compatible web search API)
26
+
27
+ - **Multiple Web Crawlers**:
28
+ - Basic HTTP Crawler
29
+ - Crawl4AI
30
+ - Jina Reader
31
+ - Tavily Crawler
32
+ - Firecrawl Crawler
33
+
34
+ - **Intelligent Content Processing**:
35
+ - LLM-based summarization
36
+ - Token-based truncation
37
+ - Relevancy scoring and sorting
38
+ - Content chunking and optimization
39
+
40
+ - **Query Refinement**:
41
+ - **BASIC Mode**: Single optimized search query
42
+ - **ADVANCED Mode**: Multiple targeted search queries for complex research
43
+
44
+ - **Performance Optimized**:
45
+ - Parallel execution of search and crawl operations
46
+ - Token limit management
47
+ - Configurable timeouts and error handling
48
+
49
+ ## Detailed Subsystem Docs
50
+
51
+ For deeper dives into each subsystem, see the dedicated READMEs:
52
+
53
+ - [Search Engines](./unique_web_search/services/search_engine/README.md) &mdash; full catalogue of supported engines, configuration, and usage examples.
54
+ - [Crawlers](./unique_web_search/services/crawlers/README.md) &mdash; comparison of crawling strategies (Basic, Crawl4AI, Tavily, Firecrawl, Jina) with setup guides.
55
+ - [Executors](./unique_web_search/services/executors/README.md) &mdash; orchestration layer (V1 & V2) covering query refinement, planning, logging, and best practices.
56
+
57
+ ## Configuration
58
+
59
+ The tool uses environment variables and configuration files to manage API keys and settings. Key configuration areas include:
60
+
61
+ - Search engine selection and API keys
62
+ - Crawler selection and configuration
63
+ - Content processing strategies (SUMMARIZE, TRUNCATE, NONE)
64
+ - Token limits and relevancy thresholds
65
+ - Proxy configuration
66
+ - Debug and monitoring options
67
+
68
+ ## Dependency management (uv.lock + min/latest testing)
69
+
70
+ This package is a **library** and uses `uv` for dependency management.
71
+
72
+ We run tests additionally with minimal dependencies to ensure that the listed ranges are valid. NOTE: We use lowest-direct, not lowest.
73
+ Lowest attempts to use the lowest possible dependency versions _tarnsitively_ causing issues if a dependency has incorrect metadata. Example:
74
+ - google-cloud-aiplatform says it works with shapely<3.0.0.
75
+ - The lowest resolver assumes 1.0 which needs python 2 -> breaks
76
+ Therefore we use lowest-direct which only sets our direct dependencies to lowest. However, this only correctly verifies our min dependencies
77
+ if our code correctly lists all the required dependencies and never imports a transitive dependency. We therefore use deptry to ansure we
78
+ don't use transitive dependencies and that we have no unused dependencies.
79
+
80
+ ### Test locally
81
+
82
+ - **Latest deps and deptry**:
83
+
84
+ ```bash
85
+ cd tool_packages/unique_web_search
86
+ uv sync
87
+ uv run pytest
88
+ uv run deptry
89
+ ```
90
+
91
+
92
+
93
+ - **Min deps**:
94
+
95
+ ```bash
96
+ cd tool_packages/unique_web_search
97
+ uv venv
98
+ # Install runtime deps at minimum versions
99
+ uv pip install -e . --resolution=lowest-direct
100
+ # Install dev deps from [dependency-groups] (we only care about runtime dep minimums)
101
+ uv export --only-group dev --no-hashes | uv pip install -r -
102
+ # Use --no-sync to prevent uv from "fixing" the versions
103
+ uv run --no-sync pytest
104
+ ```
105
+
106
+ ## Workflow
107
+
108
+ 1. **Input**: User query or structured search plan
109
+ 2. **Configuration**: Load settings and initialize services
110
+ 3. **Execution**:
111
+ - V1: Query refinement → Search → Crawl → Process
112
+ - V2: Execute planned steps in parallel → Process
113
+ 4. **Content Processing**: Clean, summarize/truncate, and chunk content
114
+ 5. **Optimization**: Reduce to token limits and sort by relevance
115
+ 6. **Output**: Return structured content chunks optimized for LLM consumption
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "unique_web_search"
3
+ version = "1.7.4"
4
+ description = ""
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [
8
+ { name = "Andreas Hauri", email = "andreas@unique.ch" },
9
+ { name = "Gustav Hartz", email = "gustav.hartz.ext@unique.ch" },
10
+ { name = "Rami Azouz", email = "rami.ext@unique.ch" },
11
+ ]
12
+ license = { text = "Proprietary" }
13
+ dependencies = [
14
+ "typing-extensions>=4.14.1,<5",
15
+ "pydantic>=2.12.3,<3",
16
+ "pydantic-settings>=2.10.1,<3",
17
+ "timeout-decorator>=0.5.0,<1",
18
+ "markdownify>=0.14.1,<1",
19
+ "fake-useragent>=2.2.0,<3",
20
+ "crawl4ai>=0.6.3,<1",
21
+ "firecrawl>=3.3.2,<4",
22
+ "tavily-python>=0.7.11,<1",
23
+ "unidecode>=1.4.0,<2",
24
+ "azure-ai-projects>=1.0.0,<2",
25
+ "azure-identity>=1.25.0,<2",
26
+ "unique-toolkit>=1.38.3,<2",
27
+ "azure-core>=1.36.0,<2",
28
+ "google-cloud-aiplatform>=1.128.0,<2",
29
+ "google-auth>=2.43.0,<3",
30
+ "google-generativeai>=0.8.5,<0.9",
31
+ "langchain-text-splitters>=1.0.0,<2",
32
+ "httpx>=0.28.1",
33
+ "tiktoken>=0.12.0",
34
+ "openai>=1.109.1",
35
+ "certifi>=2025.11.12",
36
+ ]
37
+
38
+ [build-system]
39
+ requires = ["uv_build>=0.8.14,<0.9.0"]
40
+ build-backend = "uv_build"
41
+
42
+ [dependency-groups]
43
+ dev = [
44
+ "deptry>=0.24.0",
45
+ "pytest>=8.4.1,<9",
46
+ "pytest-asyncio>=1.2.0,<2",
47
+ "pytest-mock>=3.14.0,<4",
48
+ "ruff>=0.12.10,<0.13",
49
+ ]
50
+
51
+ [tool.deptry]
52
+ known_first_party = ["unique_web_search"]
53
+
54
+ [tool.ruff]
55
+ target-version = "py312"
56
+
57
+ [tool.ruff.lint]
58
+ extend-select = ["I"]
59
+
@@ -0,0 +1,202 @@
1
+ import logging
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from unique_web_search.settings import env_settings
6
+
7
+ _LOGGER = logging.getLogger(__name__)
8
+
9
+
10
+ class GoogleSearchSettings(BaseModel):
11
+ api_key: str | None = None
12
+ search_engine_id: str | None = None
13
+ api_endpoint: str | None = None
14
+
15
+ @property
16
+ def is_configured(self) -> bool:
17
+ return (
18
+ self.api_key and self.search_engine_id and self.api_endpoint
19
+ ) is not None
20
+
21
+ @classmethod
22
+ def from_env_settings(cls):
23
+ missing_settings = []
24
+
25
+ if env_settings.google_search_api_key is None:
26
+ missing_settings.append("API Key")
27
+ if env_settings.google_search_engine_id is None:
28
+ missing_settings.append("Engine ID")
29
+ if env_settings.google_search_api_endpoint is None:
30
+ missing_settings.append("API Endpoint")
31
+
32
+ if missing_settings:
33
+ _LOGGER.warning(
34
+ f"Google Search API missing required settings: {', '.join(missing_settings)}"
35
+ )
36
+ else:
37
+ _LOGGER.info("Google Search API is properly configured")
38
+
39
+ return cls(
40
+ api_key=env_settings.google_search_api_key,
41
+ search_engine_id=env_settings.google_search_engine_id,
42
+ api_endpoint=env_settings.google_search_api_endpoint,
43
+ )
44
+
45
+
46
+ _google_search_settings: GoogleSearchSettings | None = None
47
+
48
+
49
+ def get_google_search_settings() -> GoogleSearchSettings:
50
+ global _google_search_settings
51
+ if _google_search_settings is None:
52
+ _google_search_settings = GoogleSearchSettings.from_env_settings()
53
+ return _google_search_settings
54
+
55
+
56
+ class FirecrawlSearchSettings(BaseModel):
57
+ api_key: str | None = None
58
+
59
+ @property
60
+ def is_configured(self) -> bool:
61
+ return self.api_key is not None
62
+
63
+ @classmethod
64
+ def from_env_settings(cls):
65
+ missing_settings = []
66
+
67
+ if env_settings.firecrawl_api_key is None:
68
+ missing_settings.append("API Key")
69
+
70
+ if missing_settings:
71
+ _LOGGER.warning(
72
+ f"Firecrawl Search API missing required settings: {', '.join(missing_settings)}"
73
+ )
74
+ else:
75
+ _LOGGER.info("Firecrawl Search API is properly configured")
76
+
77
+ return cls(
78
+ api_key=env_settings.firecrawl_api_key,
79
+ )
80
+
81
+
82
+ _firecrawl_search_settings: FirecrawlSearchSettings | None = None
83
+
84
+
85
+ def get_firecrawl_search_settings() -> FirecrawlSearchSettings:
86
+ global _firecrawl_search_settings
87
+ if _firecrawl_search_settings is None:
88
+ _firecrawl_search_settings = FirecrawlSearchSettings.from_env_settings()
89
+ return _firecrawl_search_settings
90
+
91
+
92
+ class JinaSearchSettings(BaseModel):
93
+ api_key: str | None = None
94
+ search_api_endpoint: str = env_settings.jina_search_api_endpoint
95
+ reader_api_endpoint: str = env_settings.jina_reader_api_endpoint
96
+
97
+ @property
98
+ def is_configured(self) -> bool:
99
+ return self.api_key is not None
100
+
101
+ @classmethod
102
+ def from_env_settings(cls):
103
+ missing_settings = []
104
+
105
+ if env_settings.jina_api_key is None:
106
+ missing_settings.append("API Key")
107
+
108
+ if missing_settings:
109
+ _LOGGER.warning(
110
+ f"Jina Search API missing required settings: {', '.join(missing_settings)}"
111
+ )
112
+ else:
113
+ _LOGGER.info("Jina Search API is properly configured")
114
+
115
+ return cls(
116
+ api_key=env_settings.jina_api_key,
117
+ )
118
+
119
+
120
+ _jina_search_settings: JinaSearchSettings | None = None
121
+
122
+
123
+ def get_jina_search_settings() -> JinaSearchSettings:
124
+ global _jina_search_settings
125
+ if _jina_search_settings is None:
126
+ _jina_search_settings = JinaSearchSettings.from_env_settings()
127
+ return _jina_search_settings
128
+
129
+
130
+ class TavilySearchSettings(BaseModel):
131
+ api_key: str | None = None
132
+
133
+ @property
134
+ def is_configured(self) -> bool:
135
+ return self.api_key is not None
136
+
137
+ @classmethod
138
+ def from_env_settings(cls):
139
+ missing_settings = []
140
+
141
+ if env_settings.tavily_api_key is None:
142
+ missing_settings.append("API Key")
143
+
144
+ if missing_settings:
145
+ _LOGGER.warning(
146
+ f"Tavily Search API missing required settings: {', '.join(missing_settings)}"
147
+ )
148
+ else:
149
+ _LOGGER.info("Tavily Search API is properly configured")
150
+
151
+ return cls(api_key=env_settings.tavily_api_key)
152
+
153
+
154
+ _tavily_search_settings: TavilySearchSettings | None = None
155
+
156
+
157
+ def get_tavily_search_settings() -> TavilySearchSettings:
158
+ global _tavily_search_settings
159
+ if _tavily_search_settings is None:
160
+ _tavily_search_settings = TavilySearchSettings.from_env_settings()
161
+ return _tavily_search_settings
162
+
163
+
164
+ class BraveSearchSettings(BaseModel):
165
+ api_key: str | None = None
166
+ api_endpoint: str | None = None
167
+
168
+ @property
169
+ def is_configured(self) -> bool:
170
+ return self.api_key is not None
171
+
172
+ @classmethod
173
+ def from_env_settings(cls):
174
+ missing_settings = []
175
+
176
+ if env_settings.brave_search_api_key is None:
177
+ missing_settings.append("API Key")
178
+ if env_settings.brave_search_api_endpoint is None:
179
+ missing_settings.append("API Endpoint")
180
+
181
+ if missing_settings:
182
+ _LOGGER.warning(
183
+ f"Brave Search API missing required settings: {', '.join(missing_settings)}"
184
+ )
185
+
186
+ else:
187
+ _LOGGER.info("Brave Search API is properly configured")
188
+
189
+ return cls(
190
+ api_key=env_settings.brave_search_api_key,
191
+ api_endpoint=env_settings.brave_search_api_endpoint,
192
+ )
193
+
194
+
195
+ _brave_search_settings: BraveSearchSettings | None = None
196
+
197
+
198
+ def get_brave_search_settings() -> BraveSearchSettings:
199
+ global _brave_search_settings
200
+ if _brave_search_settings is None:
201
+ _brave_search_settings = BraveSearchSettings.from_env_settings()
202
+ return _brave_search_settings