Skip to content

Commit adef098

Browse files
authored
feat(web search): normalize search provider secret precedence to match inference providers (#6036)
Search tools previously used config-key > provider-data-key precedence, whereas inference providers use provider-data-key > config-key. This breaks multi-tenant patterns where provider data should override config. Normalize the following search providers (brave, bing, tavily) to match inference provider behavior: provider data key always overrides config key, both are optional, and _get_api_key() returns None when neither is set. Add TestProviderDataApiKeyOverride test classes to bing, brave, and tavily. Signed-off-by: Matthew Farrellee <matt@cs.wisc.edu>
1 parent f60bd20 commit adef098

12 files changed

Lines changed: 452 additions & 232 deletions

File tree

docs/docs/providers/tool_runtime/remote_bing-search.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Bing Search tool for web search capabilities using Microsoft's search engine.
1616
|-------|------|----------|---------|-------------|
1717
| `timeout` | `float` | No | 30.0 | Overall HTTP timeout in seconds for requests to external services. |
1818
| `connect_timeout` | `float` | No | 10.0 | TCP connect timeout in seconds. Shorter than the overall timeout to fail fast on unreachable hosts. |
19-
| `api_key` | `SecretStr \| None` | No | | |
19+
| `api_key` | `SecretStr \| None` | No | | The Bing Search API Key. Can be overridden per-request via X-OGX-Provider-Data header. |
2020
| `top_k` | `int` | No | 3 | |
2121

2222
## Sample Configuration

docs/docs/providers/tool_runtime/remote_brave-search.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Brave Search tool for web search capabilities with privacy-focused results.
1616
|-------|------|----------|---------|-------------|
1717
| `timeout` | `float` | No | 30.0 | Overall HTTP timeout in seconds for requests to external services. |
1818
| `connect_timeout` | `float` | No | 10.0 | TCP connect timeout in seconds. Shorter than the overall timeout to fail fast on unreachable hosts. |
19-
| `api_key` | `SecretStr \| None` | No | | The Brave Search API Key |
19+
| `api_key` | `SecretStr \| None` | No | | The Brave Search API Key. Can be overridden per-request via X-OGX-Provider-Data header. |
2020
| `max_results` | `int` | No | 3 | The maximum number of results to return |
2121

2222
## Sample Configuration

docs/docs/providers/tool_runtime/remote_tavily-search.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Tavily Search tool for AI-optimized web search with structured results.
1616
|-------|------|----------|---------|-------------|
1717
| `timeout` | `float` | No | 30.0 | Overall HTTP timeout in seconds for requests to external services. |
1818
| `connect_timeout` | `float` | No | 10.0 | TCP connect timeout in seconds. Shorter than the overall timeout to fail fast on unreachable hosts. |
19-
| `api_key` | `SecretStr \| None` | No | | The Tavily Search API Key |
19+
| `api_key` | `SecretStr \| None` | No | | The Tavily Search API Key. Can be overridden per-request via X-OGX-Provider-Data header. |
2020
| `max_results` | `int` | No | 3 | The maximum number of results to return |
2121

2222
## Sample Configuration

src/ogx/providers/remote/tool_runtime/bing_search/bing_search.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,14 @@ async def register_toolgroup(self, toolgroup: ToolGroup) -> None:
4747
async def unregister_toolgroup(self, toolgroup_id: str) -> None:
4848
return
4949

50-
def _get_api_key(self) -> str:
51-
if self.config.api_key:
52-
return self.config.api_key.get_secret_value()
50+
def _get_api_key(self) -> str | None:
51+
api_key = self.config.api_key.get_secret_value() if self.config.api_key else None
5352

5453
provider_data = self.get_request_provider_data()
55-
if provider_data is None or not provider_data.bing_search_api_key:
56-
raise ValueError(
57-
'Pass Bing Search API Key in the header X-OGX-Provider-Data as { "bing_search_api_key": <your api key>}'
58-
)
59-
return provider_data.bing_search_api_key.get_secret_value()
54+
if provider_data and provider_data.bing_search_api_key:
55+
api_key = provider_data.bing_search_api_key.get_secret_value()
56+
57+
return api_key
6058

6159
async def list_runtime_tools(
6260
self,
@@ -87,9 +85,9 @@ async def invoke_tool(
8785
self, tool_name: str, kwargs: dict[str, Any], authorization: str | None = None
8886
) -> ToolInvocationResult:
8987
api_key = self._get_api_key()
90-
headers = {
91-
"Ocp-Apim-Subscription-Key": api_key,
92-
}
88+
headers: dict[str, str] = {}
89+
if api_key:
90+
headers["Ocp-Apim-Subscription-Key"] = api_key
9391

9492
query = kwargs["query"]
9593

src/ogx/providers/remote/tool_runtime/bing_search/config.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66

77
from typing import Any
88

9-
from pydantic import SecretStr
9+
from pydantic import Field, SecretStr
1010

1111
from ogx.providers.utils.common.http import BaseToolRuntimeConfig
1212

1313

1414
class BingSearchToolConfig(BaseToolRuntimeConfig):
1515
"""Configuration for Bing Search Tool Runtime"""
1616

17-
api_key: SecretStr | None = None
17+
api_key: SecretStr | None = Field(
18+
default=None,
19+
description="The Bing Search API Key. Can be overridden per-request via X-OGX-Provider-Data header.",
20+
)
1821
top_k: int = 3
1922

2023
@classmethod

src/ogx/providers/remote/tool_runtime/brave_search/brave_search.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,14 @@ async def register_toolgroup(self, toolgroup: ToolGroup) -> None:
3939
async def unregister_toolgroup(self, toolgroup_id: str) -> None:
4040
return
4141

42-
def _get_api_key(self) -> str:
43-
if self.config.api_key:
44-
return self.config.api_key.get_secret_value()
42+
def _get_api_key(self) -> str | None:
43+
api_key = self.config.api_key.get_secret_value() if self.config.api_key else None
4544

4645
provider_data = self.get_request_provider_data()
47-
if provider_data is None or not provider_data.brave_search_api_key:
48-
raise ValueError(
49-
'Pass Search provider\'s API Key in the header X-OGX-Provider-Data as { "brave_search_api_key": <your api key>}'
50-
)
51-
return provider_data.brave_search_api_key.get_secret_value()
46+
if provider_data and provider_data.brave_search_api_key:
47+
api_key = provider_data.brave_search_api_key.get_secret_value()
48+
49+
return api_key
5250

5351
async def list_runtime_tools(
5452
self,
@@ -80,11 +78,12 @@ async def invoke_tool(
8078
) -> ToolInvocationResult:
8179
api_key = self._get_api_key()
8280
url = "https://api.search.brave.com/res/v1/web/search"
83-
headers = {
84-
"X-Subscription-Token": api_key,
81+
headers: dict[str, str] = {
8582
"Accept-Encoding": "gzip",
8683
"Accept": "application/json",
8784
}
85+
if api_key:
86+
headers["X-Subscription-Token"] = api_key
8887

8988
query = kwargs["query"]
9089

src/ogx/providers/remote/tool_runtime/brave_search/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class BraveSearchToolConfig(BaseToolRuntimeConfig):
1616

1717
api_key: SecretStr | None = Field(
1818
default=None,
19-
description="The Brave Search API Key",
19+
description="The Brave Search API Key. Can be overridden per-request via X-OGX-Provider-Data header.",
2020
)
2121
max_results: int = Field(
2222
default=3,

src/ogx/providers/remote/tool_runtime/tavily_search/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class TavilySearchToolConfig(BaseToolRuntimeConfig):
1616

1717
api_key: SecretStr | None = Field(
1818
default=None,
19-
description="The Tavily Search API Key",
19+
description="The Tavily Search API Key. Can be overridden per-request via X-OGX-Provider-Data header.",
2020
)
2121
max_results: int = Field(
2222
default=3,

src/ogx/providers/remote/tool_runtime/tavily_search/tavily_search.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,14 @@ async def register_toolgroup(self, toolgroup: ToolGroup) -> None:
4646
async def unregister_toolgroup(self, toolgroup_id: str) -> None:
4747
return
4848

49-
def _get_api_key(self) -> str:
50-
if self.config.api_key:
51-
return self.config.api_key.get_secret_value()
49+
def _get_api_key(self) -> str | None:
50+
api_key = self.config.api_key.get_secret_value() if self.config.api_key else None
5251

5352
provider_data = self.get_request_provider_data()
54-
if provider_data is None or not provider_data.tavily_search_api_key:
55-
raise ValueError(
56-
'Pass Search provider\'s API Key in the header X-OGX-Provider-Data as { "tavily_search_api_key": <your api key>}'
57-
)
58-
return provider_data.tavily_search_api_key.get_secret_value()
53+
if provider_data and provider_data.tavily_search_api_key:
54+
api_key = provider_data.tavily_search_api_key.get_secret_value()
55+
56+
return api_key
5957

6058
async def list_runtime_tools(
6159
self,
@@ -87,9 +85,10 @@ async def invoke_tool(
8785
) -> ToolInvocationResult:
8886
api_key = self._get_api_key()
8987
request_body: dict[str, Any] = {
90-
"api_key": api_key,
9188
"query": kwargs["query"],
9289
}
90+
if api_key:
91+
request_body["api_key"] = api_key
9392

9493
allowed_domains = kwargs.get("allowed_domains")
9594
if allowed_domains:

0 commit comments

Comments
 (0)