unique-search-proxy-core 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.
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/PKG-INFO +12 -11
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/README.md +10 -9
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/pyproject.toml +6 -2
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/base.py +15 -4
- unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/bing/__init__.py +21 -0
- unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/bing/grounding.py +74 -0
- unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/bing/schema.py +231 -0
- unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/vertexai/schema.py +74 -0
- unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/vertexai/settings.py +52 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/param_policy/derive.py +3 -2
- unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/param_policy/exposable_config.py +107 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/param_policy/exposable_param.py +11 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/base.py +9 -73
- unique_search_proxy_core-2026.36.0.dev2/unique_search_proxy_core/agent_engines/bing/__init__.py +0 -6
- unique_search_proxy_core-2026.36.0.dev2/unique_search_proxy_core/agent_engines/bing/schema.py +0 -37
- unique_search_proxy_core-2026.36.0.dev2/unique_search_proxy_core/agent_engines/vertexai/schema.py +0 -45
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/config_types.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/output_schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/resolve.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/response_parsing.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/agent_engines/vertexai/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/context.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/base.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/basic/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/basic/content_types.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/basic/processing/policy.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/basic/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/config_types.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/firecrawl/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/jina/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/crawlers/tavily/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/errors.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/logging.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/param_policy/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/param_policy/annotations.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/param_policy/exposed_params.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/param_policy/request_base.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/providers/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/brave/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/brave/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/config_types.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/google/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/google/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/perplexity/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/search_engines/perplexity/schema.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/__init__.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/dns.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/egress.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/models.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/netloc.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/policy.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/redirect.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/resolver.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/service.py +0 -0
- {unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/unique_search_proxy_core/url_safety/settings.py +0 -0
{unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-search-proxy-core
|
|
3
|
-
Version: 2026.36.0.
|
|
3
|
+
Version: 2026.36.0.dev4
|
|
4
4
|
Summary: Shared Pydantic types for the Unique Search Proxy API
|
|
5
5
|
Author: ThePhilAz
|
|
6
6
|
Author-email: ThePhilAz <rami.azouz@philico.com>
|
|
@@ -8,7 +8,7 @@ Requires-Dist: httpx>=0.28.0,<0.29
|
|
|
8
8
|
Requires-Dist: pydantic>=2.12.5,<3.0.0
|
|
9
9
|
Requires-Dist: pydantic-settings>=2.10.1,<3
|
|
10
10
|
Requires-Dist: pyhumps>=3.8.0,<4
|
|
11
|
-
Requires-Dist: unique-toolkit[monitoring]>=2026.36.0.
|
|
11
|
+
Requires-Dist: unique-toolkit[monitoring]>=2026.36.0.dev12,<2026.36.0rc0
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
|
|
@@ -65,7 +65,7 @@ System overview → [../README.md](../README.md)
|
|
|
65
65
|
|
|
66
66
|
**The config class owns its entire parameter lifecycle.** Every derived surface is a method on the deployment config — there are no standalone projection or resolver modules.
|
|
67
67
|
|
|
68
|
-
### 3.1 Config-owned API (search engines)
|
|
68
|
+
### 3.1 Config-owned API (search + agent engines)
|
|
69
69
|
|
|
70
70
|
```
|
|
71
71
|
Admin JSON → GoogleConfig {expose, value}
|
|
@@ -78,15 +78,15 @@ Admin JSON → GoogleConfig {expose, value}
|
|
|
78
78
|
| Method | Kind | Returns |
|
|
79
79
|
|--------|------|---------|
|
|
80
80
|
| `request_model()` (classmethod, cached) | search / agent / crawl | `SearchRequestBase` (required `query`) or `CrawlRequestBase` (required `urls`) + config fields; `ExposableParam` knobs unwrapped to optional plain types |
|
|
81
|
-
| `exposed_params_model()` (instance) | search
|
|
82
|
-
| `merge(overrides, *, query)` (instance) | search
|
|
81
|
+
| `exposed_params_model()` (instance) | search / agent | `ExposedParams` subclass with exactly the `expose=True` knobs (camelCase aliases, description-only schema), or `None` |
|
|
82
|
+
| `merge(overrides, *, query)` (instance) | search / agent | deployment defaults + LLM overrides + query → validated `request_model()` instance |
|
|
83
83
|
| `provider_query_params(request)` (classmethod) | search only | request serialized for the upstream provider, minus `_provider_param_exclude_fields` (Google adds `search_engine_id`) |
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
`ExposableParamsConfig` (in `param_policy/exposable_config.py`) implements the exposable-parameter lifecycle once; `BaseSearchEngineConfig` and `BaseAgentEngineConfig` both extend it. Crawlers (`BasicConfig`, …) only implement `request_model()`: they have no exposable params and no `merge` — a crawl request is simply the deployment config fields plus `urls`.
|
|
86
86
|
|
|
87
|
-
### 3.2 ExposableParam
|
|
87
|
+
### 3.2 ExposableParam
|
|
88
88
|
|
|
89
|
-
Optional **search** parameters use `ExposableParam[T]`:
|
|
89
|
+
Optional **search** and **agent** parameters use `ExposableParam[T]`:
|
|
90
90
|
|
|
91
91
|
- **`value`** — admin default merged into every request (`null` = deactivated)
|
|
92
92
|
- **`expose`** — when `true`, the parameter appears on the LLM-facing exposed-params model
|
|
@@ -153,13 +153,14 @@ flowchart TB
|
|
|
153
153
|
| `schema.py` | Shared API models: `SearchResponse`, `AgentSearchResponse`, `CrawlResponse`, `WebSearchResult`, `ErrorResponse`, SSE events |
|
|
154
154
|
| `errors.py` | `ProxyError` hierarchy and stable `ProxyErrorCode` enum |
|
|
155
155
|
| `param_policy/exposable_param.py` | `ExposableParam` value object, factory-default merge, type introspection, OpenAPI naming |
|
|
156
|
+
| `param_policy/exposable_config.py` | `ExposableParamsConfig` — the `exposed_params_model()` / `merge()` lifecycle shared by search and agent config bases |
|
|
156
157
|
| `param_policy/exposed_params.py` | `ExposedParams` base for LLM-facing parameter models (schema noise stripping) |
|
|
157
158
|
| `param_policy/request_base.py` | `SearchRequestBase` / `AgentRequestBase` / `CrawlRequestBase` (required leading fields) |
|
|
158
159
|
| `param_policy/annotations.py` | Annotation unwrapping helpers (private, used by `derive`) |
|
|
159
160
|
| `param_policy/derive.py` | `derive_request_model` / `derive_exposed_params_model` factories called by the config base classes |
|
|
160
161
|
| `providers/schema.py` | JSON Schema + defaults for deployment UIs (`provider_config_json_schema`, …) |
|
|
161
162
|
| `search_engines/` | Config models with the config-owned API (`request_model` / `exposed_params_model` / `merge` / `provider_query_params`), request union |
|
|
162
|
-
| `agent_engines/` | Agent config/request models (`request_model`), output schema |
|
|
163
|
+
| `agent_engines/` | Agent config/request models (`request_model`, `exposed_params_model`, `merge`), output schema, Bing grounding configuration + agent naming |
|
|
163
164
|
| `crawlers/` | `*Config` deployment models + derived `*CrawlRequest` bodies (`request_model()`) |
|
|
164
165
|
|
|
165
166
|
---
|
|
@@ -223,8 +224,8 @@ from unique_search_proxy_core import (
|
|
|
223
224
|
## 7. Features summary
|
|
224
225
|
|
|
225
226
|
- Discriminated provider configs (`engine`, `crawler` Literal discriminators)
|
|
226
|
-
- **Search
|
|
227
|
-
- **Agent:** `BingAgentConfig.request_model()` (injects `query`; excludes `output_schema`)
|
|
227
|
+
- **Search & agent:** `ExposableParam` policy; config-owned `request_model` / `exposed_params_model` / `merge` (`provider_query_params` is search-only)
|
|
228
|
+
- **Agent:** `BingAgentConfig.request_model()` (injects `query`; excludes `output_schema`); Bing exposes `market` / `setLang` / `freshness`, which are baked into the hashed agent name (`market` and `setLang` are `Literal`s over Bing's documented codes, like `BraveCountry`)
|
|
228
229
|
- **Crawl:** `BasicConfig.request_model()` (injects `urls`); no exposable params / no merge
|
|
229
230
|
- CamelCase JSON aliases on all models
|
|
230
231
|
- Zero server dependencies (import-linter enforced in the client package)
|
{unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/README.md
RENAMED
|
@@ -51,7 +51,7 @@ System overview → [../README.md](../README.md)
|
|
|
51
51
|
|
|
52
52
|
**The config class owns its entire parameter lifecycle.** Every derived surface is a method on the deployment config — there are no standalone projection or resolver modules.
|
|
53
53
|
|
|
54
|
-
### 3.1 Config-owned API (search engines)
|
|
54
|
+
### 3.1 Config-owned API (search + agent engines)
|
|
55
55
|
|
|
56
56
|
```
|
|
57
57
|
Admin JSON → GoogleConfig {expose, value}
|
|
@@ -64,15 +64,15 @@ Admin JSON → GoogleConfig {expose, value}
|
|
|
64
64
|
| Method | Kind | Returns |
|
|
65
65
|
|--------|------|---------|
|
|
66
66
|
| `request_model()` (classmethod, cached) | search / agent / crawl | `SearchRequestBase` (required `query`) or `CrawlRequestBase` (required `urls`) + config fields; `ExposableParam` knobs unwrapped to optional plain types |
|
|
67
|
-
| `exposed_params_model()` (instance) | search
|
|
68
|
-
| `merge(overrides, *, query)` (instance) | search
|
|
67
|
+
| `exposed_params_model()` (instance) | search / agent | `ExposedParams` subclass with exactly the `expose=True` knobs (camelCase aliases, description-only schema), or `None` |
|
|
68
|
+
| `merge(overrides, *, query)` (instance) | search / agent | deployment defaults + LLM overrides + query → validated `request_model()` instance |
|
|
69
69
|
| `provider_query_params(request)` (classmethod) | search only | request serialized for the upstream provider, minus `_provider_param_exclude_fields` (Google adds `search_engine_id`) |
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
`ExposableParamsConfig` (in `param_policy/exposable_config.py`) implements the exposable-parameter lifecycle once; `BaseSearchEngineConfig` and `BaseAgentEngineConfig` both extend it. Crawlers (`BasicConfig`, …) only implement `request_model()`: they have no exposable params and no `merge` — a crawl request is simply the deployment config fields plus `urls`.
|
|
72
72
|
|
|
73
|
-
### 3.2 ExposableParam
|
|
73
|
+
### 3.2 ExposableParam
|
|
74
74
|
|
|
75
|
-
Optional **search** parameters use `ExposableParam[T]`:
|
|
75
|
+
Optional **search** and **agent** parameters use `ExposableParam[T]`:
|
|
76
76
|
|
|
77
77
|
- **`value`** — admin default merged into every request (`null` = deactivated)
|
|
78
78
|
- **`expose`** — when `true`, the parameter appears on the LLM-facing exposed-params model
|
|
@@ -139,13 +139,14 @@ flowchart TB
|
|
|
139
139
|
| `schema.py` | Shared API models: `SearchResponse`, `AgentSearchResponse`, `CrawlResponse`, `WebSearchResult`, `ErrorResponse`, SSE events |
|
|
140
140
|
| `errors.py` | `ProxyError` hierarchy and stable `ProxyErrorCode` enum |
|
|
141
141
|
| `param_policy/exposable_param.py` | `ExposableParam` value object, factory-default merge, type introspection, OpenAPI naming |
|
|
142
|
+
| `param_policy/exposable_config.py` | `ExposableParamsConfig` — the `exposed_params_model()` / `merge()` lifecycle shared by search and agent config bases |
|
|
142
143
|
| `param_policy/exposed_params.py` | `ExposedParams` base for LLM-facing parameter models (schema noise stripping) |
|
|
143
144
|
| `param_policy/request_base.py` | `SearchRequestBase` / `AgentRequestBase` / `CrawlRequestBase` (required leading fields) |
|
|
144
145
|
| `param_policy/annotations.py` | Annotation unwrapping helpers (private, used by `derive`) |
|
|
145
146
|
| `param_policy/derive.py` | `derive_request_model` / `derive_exposed_params_model` factories called by the config base classes |
|
|
146
147
|
| `providers/schema.py` | JSON Schema + defaults for deployment UIs (`provider_config_json_schema`, …) |
|
|
147
148
|
| `search_engines/` | Config models with the config-owned API (`request_model` / `exposed_params_model` / `merge` / `provider_query_params`), request union |
|
|
148
|
-
| `agent_engines/` | Agent config/request models (`request_model`), output schema |
|
|
149
|
+
| `agent_engines/` | Agent config/request models (`request_model`, `exposed_params_model`, `merge`), output schema, Bing grounding configuration + agent naming |
|
|
149
150
|
| `crawlers/` | `*Config` deployment models + derived `*CrawlRequest` bodies (`request_model()`) |
|
|
150
151
|
|
|
151
152
|
---
|
|
@@ -209,8 +210,8 @@ from unique_search_proxy_core import (
|
|
|
209
210
|
## 7. Features summary
|
|
210
211
|
|
|
211
212
|
- Discriminated provider configs (`engine`, `crawler` Literal discriminators)
|
|
212
|
-
- **Search
|
|
213
|
-
- **Agent:** `BingAgentConfig.request_model()` (injects `query`; excludes `output_schema`)
|
|
213
|
+
- **Search & agent:** `ExposableParam` policy; config-owned `request_model` / `exposed_params_model` / `merge` (`provider_query_params` is search-only)
|
|
214
|
+
- **Agent:** `BingAgentConfig.request_model()` (injects `query`; excludes `output_schema`); Bing exposes `market` / `setLang` / `freshness`, which are baked into the hashed agent name (`market` and `setLang` are `Literal`s over Bing's documented codes, like `BraveCountry`)
|
|
214
215
|
- **Crawl:** `BasicConfig.request_model()` (injects `urls`); no exposable params / no merge
|
|
215
216
|
- CamelCase JSON aliases on all models
|
|
216
217
|
- Zero server dependencies (import-linter enforced in the client package)
|
{unique_search_proxy_core-2026.36.0.dev2 → unique_search_proxy_core-2026.36.0.dev4}/pyproject.toml
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique-search-proxy-core"
|
|
3
|
-
version = "2026.36.0.
|
|
3
|
+
version = "2026.36.0.dev4"
|
|
4
4
|
description = "Shared Pydantic types for the Unique Search Proxy API"
|
|
5
5
|
authors = [{ name = "ThePhilAz", email = "rami.azouz@philico.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"pydantic>=2.12.5,<3.0.0",
|
|
11
11
|
"pydantic-settings>=2.10.1,<3",
|
|
12
12
|
"pyhumps>=3.8.0,<4",
|
|
13
|
-
"unique-toolkit[monitoring]>=2026.36.0.
|
|
13
|
+
"unique-toolkit[monitoring]>=2026.36.0.dev12,<2026.36.0rc0",
|
|
14
14
|
|
|
15
15
|
]
|
|
16
16
|
|
|
@@ -61,6 +61,10 @@ depcheck = "deptry ."
|
|
|
61
61
|
[tool.pytest.ini_options]
|
|
62
62
|
addopts = "--strict-markers --import-mode=importlib"
|
|
63
63
|
asyncio_mode = "auto"
|
|
64
|
+
markers = [
|
|
65
|
+
"ai: AI-authored or AI-generated tests",
|
|
66
|
+
"unit: unit tests",
|
|
67
|
+
]
|
|
64
68
|
|
|
65
69
|
[tool.uv.sources]
|
|
66
70
|
unique-toolkit = { workspace = true }
|
|
@@ -10,11 +10,13 @@ from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
|
10
10
|
|
|
11
11
|
from unique_search_proxy_core.agent_engines.output_schema import AgentSearchOutput
|
|
12
12
|
from unique_search_proxy_core.param_policy.derive import derive_request_model
|
|
13
|
+
from unique_search_proxy_core.param_policy.exposable_config import (
|
|
14
|
+
ExposableParamsConfig,
|
|
15
|
+
)
|
|
13
16
|
from unique_search_proxy_core.param_policy.request_base import AgentRequestBase
|
|
14
17
|
from unique_search_proxy_core.schema import (
|
|
15
18
|
AgentSearchResponse,
|
|
16
19
|
AgentSearchStreamEvent,
|
|
17
|
-
camelized_model_config,
|
|
18
20
|
)
|
|
19
21
|
|
|
20
22
|
if TYPE_CHECKING:
|
|
@@ -48,14 +50,23 @@ class AgentEngineType(StrEnum):
|
|
|
48
50
|
_AGENT_REQUEST_EXCLUDED_FIELDS = frozenset({"output_schema"})
|
|
49
51
|
|
|
50
52
|
|
|
51
|
-
class BaseAgentEngineConfig(
|
|
52
|
-
"""Shared agent-engine config; each engine narrows ``engine`` with a Literal.
|
|
53
|
+
class BaseAgentEngineConfig(ExposableParamsConfig, Generic[T]):
|
|
54
|
+
"""Shared agent-engine config; each engine narrows ``engine`` with a Literal.
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
Like the standard search-engine config, this owns the whole parameter
|
|
57
|
+
lifecycle: :meth:`request_model` for the HTTP body, and (from
|
|
58
|
+
:class:`ExposableParamsConfig`) ``exposed_params_model()`` / ``merge()`` for
|
|
59
|
+
engines that publish ``ExposableParam`` knobs.
|
|
60
|
+
"""
|
|
55
61
|
|
|
56
62
|
#: Name of the derived request model; set by every concrete agent config.
|
|
57
63
|
_request_model_name: ClassVar[str]
|
|
58
64
|
|
|
65
|
+
#: ``output_schema`` is a server-side concern, never a request default.
|
|
66
|
+
_merge_exclude_fields: ClassVar[frozenset[str]] = (
|
|
67
|
+
ExposableParamsConfig._merge_exclude_fields | _AGENT_REQUEST_EXCLUDED_FIELDS
|
|
68
|
+
)
|
|
69
|
+
|
|
59
70
|
@classmethod
|
|
60
71
|
def request_model(cls) -> type[BaseModel]:
|
|
61
72
|
"""HTTP request body model, cached per config class.
|
unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/bing/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from unique_search_proxy_core.agent_engines.bing.grounding import (
|
|
2
|
+
BING_AUTO_AGENT_NAME_PREFIX,
|
|
3
|
+
BingGroundingConfiguration,
|
|
4
|
+
bing_agent_config_hash,
|
|
5
|
+
bing_agent_name,
|
|
6
|
+
is_auto_provisioned_bing_agent_name,
|
|
7
|
+
)
|
|
8
|
+
from unique_search_proxy_core.agent_engines.bing.schema import (
|
|
9
|
+
BingAgentConfig,
|
|
10
|
+
BingAgentSearchRequest,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"BING_AUTO_AGENT_NAME_PREFIX",
|
|
15
|
+
"BingAgentConfig",
|
|
16
|
+
"BingAgentSearchRequest",
|
|
17
|
+
"BingGroundingConfiguration",
|
|
18
|
+
"bing_agent_config_hash",
|
|
19
|
+
"bing_agent_name",
|
|
20
|
+
"is_auto_provisioned_bing_agent_name",
|
|
21
|
+
]
|
unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/bing/grounding.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Bing grounding tool configuration shared by the proxy and legacy runners.
|
|
2
|
+
|
|
3
|
+
Azure bakes these knobs into an *agent version* at creation time — they are not
|
|
4
|
+
sent per request — so every runner derives its agent name from them. Keeping the
|
|
5
|
+
value object and its hash payload here means the proxy client and the web-search
|
|
6
|
+
tool cannot drift on what a given agent name stands for.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
from dataclasses import astuple, dataclass
|
|
13
|
+
|
|
14
|
+
#: Name prefix of every auto-provisioned Bing grounding agent.
|
|
15
|
+
BING_AUTO_AGENT_NAME_PREFIX = "unique-grounding-with-bing"
|
|
16
|
+
|
|
17
|
+
_CONFIG_HASH_LENGTH = 12
|
|
18
|
+
_HASH_FIELD_SEPARATOR = "\0"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class BingGroundingConfiguration:
|
|
23
|
+
"""Knobs forwarded to ``BingGroundingSearchConfiguration`` on the agent tool."""
|
|
24
|
+
|
|
25
|
+
fetch_size: int
|
|
26
|
+
market: str | None = None
|
|
27
|
+
set_lang: str | None = None
|
|
28
|
+
freshness: str | None = None
|
|
29
|
+
|
|
30
|
+
def hash_payload(self) -> str:
|
|
31
|
+
"""Stable string identifying this tool configuration."""
|
|
32
|
+
return _HASH_FIELD_SEPARATOR.join(str(value) for value in astuple(self))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def bing_agent_config_hash(
|
|
36
|
+
*,
|
|
37
|
+
model: str,
|
|
38
|
+
instructions: str,
|
|
39
|
+
grounding: BingGroundingConfiguration,
|
|
40
|
+
) -> str:
|
|
41
|
+
"""Return a short hex digest of everything baked into the agent version."""
|
|
42
|
+
payload = _HASH_FIELD_SEPARATOR.join(
|
|
43
|
+
(model, instructions, grounding.hash_payload()),
|
|
44
|
+
).encode()
|
|
45
|
+
return hashlib.sha256(payload).hexdigest()[:_CONFIG_HASH_LENGTH]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def bing_agent_name(
|
|
49
|
+
*,
|
|
50
|
+
model: str,
|
|
51
|
+
instructions: str,
|
|
52
|
+
grounding: BingGroundingConfiguration,
|
|
53
|
+
) -> str:
|
|
54
|
+
"""Build a Foundry-safe agent name unique to this configuration."""
|
|
55
|
+
digest = bing_agent_config_hash(
|
|
56
|
+
model=model,
|
|
57
|
+
instructions=instructions,
|
|
58
|
+
grounding=grounding,
|
|
59
|
+
)
|
|
60
|
+
return f"{BING_AUTO_AGENT_NAME_PREFIX}-{digest}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def is_auto_provisioned_bing_agent_name(name: str) -> bool:
|
|
64
|
+
"""True for names this module would have generated."""
|
|
65
|
+
return name.startswith(f"{BING_AUTO_AGENT_NAME_PREFIX}-")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"BING_AUTO_AGENT_NAME_PREFIX",
|
|
70
|
+
"BingGroundingConfiguration",
|
|
71
|
+
"bing_agent_config_hash",
|
|
72
|
+
"bing_agent_name",
|
|
73
|
+
"is_auto_provisioned_bing_agent_name",
|
|
74
|
+
]
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, Any, ClassVar, Literal, TypeAlias
|
|
4
|
+
|
|
5
|
+
from pydantic import Field, GetCoreSchemaHandler
|
|
6
|
+
from pydantic_core import CoreSchema, core_schema
|
|
7
|
+
from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
8
|
+
|
|
9
|
+
from unique_search_proxy_core.agent_engines.base import (
|
|
10
|
+
AgentEngineType,
|
|
11
|
+
BaseAgentEngineConfig,
|
|
12
|
+
)
|
|
13
|
+
from unique_search_proxy_core.param_policy.exposable_param import ExposableParam
|
|
14
|
+
from unique_search_proxy_core.schema import DeactivatedNone
|
|
15
|
+
|
|
16
|
+
_BING_DOCS_BASE_URL = (
|
|
17
|
+
"https://learn.microsoft.com/en-us/previous-versions/bing/search-apis/"
|
|
18
|
+
"bing-web-search/reference"
|
|
19
|
+
)
|
|
20
|
+
_BING_QUERY_PARAMS_DOCS_URL = f"{_BING_DOCS_BASE_URL}/query-parameters"
|
|
21
|
+
_BING_MARKET_CODES_DOCS_URL = f"{_BING_DOCS_BASE_URL}/market-codes"
|
|
22
|
+
|
|
23
|
+
#: Markets Bing serves content for (``mkt``), in the documented order.
|
|
24
|
+
BingMarket: TypeAlias = Literal[
|
|
25
|
+
"es-AR",
|
|
26
|
+
"en-AU",
|
|
27
|
+
"de-AT",
|
|
28
|
+
"nl-BE",
|
|
29
|
+
"fr-BE",
|
|
30
|
+
"pt-BR",
|
|
31
|
+
"en-CA",
|
|
32
|
+
"fr-CA",
|
|
33
|
+
"es-CL",
|
|
34
|
+
"da-DK",
|
|
35
|
+
"fi-FI",
|
|
36
|
+
"fr-FR",
|
|
37
|
+
"de-DE",
|
|
38
|
+
"zh-HK",
|
|
39
|
+
"en-IN",
|
|
40
|
+
"en-ID",
|
|
41
|
+
"it-IT",
|
|
42
|
+
"ja-JP",
|
|
43
|
+
"ko-KR",
|
|
44
|
+
"en-MY",
|
|
45
|
+
"es-MX",
|
|
46
|
+
"nl-NL",
|
|
47
|
+
"en-NZ",
|
|
48
|
+
"no-NO",
|
|
49
|
+
"zh-CN",
|
|
50
|
+
"pl-PL",
|
|
51
|
+
"en-PH",
|
|
52
|
+
"ru-RU",
|
|
53
|
+
"en-ZA",
|
|
54
|
+
"es-ES",
|
|
55
|
+
"sv-SE",
|
|
56
|
+
"fr-CH",
|
|
57
|
+
"de-CH",
|
|
58
|
+
"zh-TW",
|
|
59
|
+
"tr-TR",
|
|
60
|
+
"en-GB",
|
|
61
|
+
"en-US",
|
|
62
|
+
"es-US",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
#: Languages Bing localizes its interface strings into (``setLang``).
|
|
66
|
+
BingSetLang: TypeAlias = Literal[
|
|
67
|
+
"ar",
|
|
68
|
+
"eu",
|
|
69
|
+
"bn",
|
|
70
|
+
"bg",
|
|
71
|
+
"ca",
|
|
72
|
+
"zh-hans",
|
|
73
|
+
"zh-hant",
|
|
74
|
+
"hr",
|
|
75
|
+
"cs",
|
|
76
|
+
"da",
|
|
77
|
+
"nl",
|
|
78
|
+
"en",
|
|
79
|
+
"en-gb",
|
|
80
|
+
"et",
|
|
81
|
+
"fi",
|
|
82
|
+
"fr",
|
|
83
|
+
"gl",
|
|
84
|
+
"de",
|
|
85
|
+
"gu",
|
|
86
|
+
"he",
|
|
87
|
+
"hi",
|
|
88
|
+
"hu",
|
|
89
|
+
"is",
|
|
90
|
+
"it",
|
|
91
|
+
"jp",
|
|
92
|
+
"kn",
|
|
93
|
+
"ko",
|
|
94
|
+
"lv",
|
|
95
|
+
"lt",
|
|
96
|
+
"ms",
|
|
97
|
+
"ml",
|
|
98
|
+
"mr",
|
|
99
|
+
"nb",
|
|
100
|
+
"pl",
|
|
101
|
+
"pt-br",
|
|
102
|
+
"pt-pt",
|
|
103
|
+
"pa",
|
|
104
|
+
"ro",
|
|
105
|
+
"ru",
|
|
106
|
+
"sr",
|
|
107
|
+
"sk",
|
|
108
|
+
"sl",
|
|
109
|
+
"es",
|
|
110
|
+
"sv",
|
|
111
|
+
"ta",
|
|
112
|
+
"te",
|
|
113
|
+
"th",
|
|
114
|
+
"tr",
|
|
115
|
+
"uk",
|
|
116
|
+
"vi",
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
#: Named recency windows accepted by the Bing ``freshness`` knob.
|
|
120
|
+
BingFreshnessPreset: TypeAlias = Literal["Day", "Week", "Month"]
|
|
121
|
+
|
|
122
|
+
_FRESHNESS_DATE_PATTERN = r"^\d{4}-\d{2}-\d{2}(?:\.\.\d{4}-\d{2}-\d{2})?$"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class BingFreshnessDate(str):
|
|
126
|
+
"""A single ``YYYY-MM-DD`` day or an inclusive ``YYYY-MM-DD..YYYY-MM-DD`` span.
|
|
127
|
+
|
|
128
|
+
The constraint lives in the type instead of ``Field(pattern=...)`` because
|
|
129
|
+
request-model derivation strips ``Annotated`` metadata off union branches,
|
|
130
|
+
which would leave exactly the surface the LLM writes to unchecked. Freshness
|
|
131
|
+
is baked into the hashed agent name, so free text here would mint a Foundry
|
|
132
|
+
agent version per distinct string while Bing ignored the value itself.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def __get_pydantic_core_schema__(
|
|
137
|
+
cls,
|
|
138
|
+
source: type[Any],
|
|
139
|
+
handler: GetCoreSchemaHandler,
|
|
140
|
+
) -> CoreSchema:
|
|
141
|
+
return core_schema.str_schema(pattern=_FRESHNESS_DATE_PATTERN)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
MarketOrNone: TypeAlias = Annotated[BingMarket, Field(title="Market")] | DeactivatedNone
|
|
145
|
+
SetLangOrNone: TypeAlias = (
|
|
146
|
+
Annotated[BingSetLang, Field(title="Language")] | DeactivatedNone
|
|
147
|
+
)
|
|
148
|
+
FreshnessOrNone: TypeAlias = (
|
|
149
|
+
Annotated[BingFreshnessPreset, Field(title="Preset")]
|
|
150
|
+
| Annotated[BingFreshnessDate, Field(title="Day or range")]
|
|
151
|
+
| DeactivatedNone
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
ExposableMarket = ExposableParam[MarketOrNone]
|
|
155
|
+
ExposableSetLang = ExposableParam[SetLangOrNone]
|
|
156
|
+
ExposableFreshness = ExposableParam[FreshnessOrNone]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class BingAgentConfig(BaseAgentEngineConfig[Literal[AgentEngineType.BING]]):
|
|
160
|
+
"""Deployment + request defaults for Bing grounding via Azure AI Projects."""
|
|
161
|
+
|
|
162
|
+
_request_model_name: ClassVar[str] = "BingAgentSearchRequest"
|
|
163
|
+
_exposed_params_model_name: ClassVar[str] = "BingAgentExposedParams"
|
|
164
|
+
|
|
165
|
+
engine: Annotated[
|
|
166
|
+
Literal[AgentEngineType.BING], RJSFMetaTag.SpecialWidget.hidden()
|
|
167
|
+
] = Field(
|
|
168
|
+
default=AgentEngineType.BING,
|
|
169
|
+
title="Agent engine",
|
|
170
|
+
description="Provider discriminator; must be `bing` for this config.",
|
|
171
|
+
)
|
|
172
|
+
fetch_size: int = Field(
|
|
173
|
+
default=5,
|
|
174
|
+
ge=1,
|
|
175
|
+
le=50,
|
|
176
|
+
description="Maximum number of Bing grounding results per query",
|
|
177
|
+
)
|
|
178
|
+
market: ExposableMarket = Field(
|
|
179
|
+
default=ExposableMarket(expose=False, value=None),
|
|
180
|
+
title="Market",
|
|
181
|
+
description=(
|
|
182
|
+
"Country/region **and** language the results come from (Bing `mkt`), "
|
|
183
|
+
"as `<language>-<country>`: `de-CH` returns German-language Swiss "
|
|
184
|
+
"results, `fr-CH` French-language Swiss ones, `en-GB` UK English. "
|
|
185
|
+
"Set it when the question is about a specific country or expects an "
|
|
186
|
+
"answer in that country's language. Left unset, Bing guesses the "
|
|
187
|
+
"market from the caller and may answer from another country. "
|
|
188
|
+
f"[Market codes]({_BING_MARKET_CODES_DOCS_URL})"
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
set_lang: ExposableSetLang = Field(
|
|
192
|
+
default=ExposableSetLang(expose=False, value=None),
|
|
193
|
+
title="Interface language",
|
|
194
|
+
description=(
|
|
195
|
+
"Language of Bing's own interface strings in the response (Bing "
|
|
196
|
+
"`setLang`), e.g. `de`, `fr`, `pt-br`. It changes neither which "
|
|
197
|
+
"results come back nor the language they are written in — use the "
|
|
198
|
+
"market for that. Bing falls back to English for codes it does not "
|
|
199
|
+
"support. "
|
|
200
|
+
f"[Supported languages]({_BING_MARKET_CODES_DOCS_URL}#bing-supported-language-codes)"
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
freshness: ExposableFreshness = Field(
|
|
204
|
+
default=ExposableFreshness(expose=False, value=None),
|
|
205
|
+
title="Freshness",
|
|
206
|
+
description=(
|
|
207
|
+
"Keep only pages Bing discovered recently (Bing `freshness`): `Day` "
|
|
208
|
+
"(last 24 hours), `Week` (last 7 days), `Month` (last 30 days), a "
|
|
209
|
+
"single `YYYY-MM-DD` day, or an inclusive "
|
|
210
|
+
"`YYYY-MM-DD..YYYY-MM-DD` range. Use it for news and fast-moving "
|
|
211
|
+
"topics; leave it unset for background or reference questions, where "
|
|
212
|
+
"it would hide older pages that are still correct. "
|
|
213
|
+
f"[Accepted values]({_BING_QUERY_PARAMS_DOCS_URL}#freshness)"
|
|
214
|
+
),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
BingAgentSearchRequest = BingAgentConfig.request_model()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
__all__ = [
|
|
222
|
+
"BingAgentConfig",
|
|
223
|
+
"BingAgentSearchRequest",
|
|
224
|
+
"BingFreshnessDate",
|
|
225
|
+
"BingFreshnessPreset",
|
|
226
|
+
"BingMarket",
|
|
227
|
+
"BingSetLang",
|
|
228
|
+
"ExposableFreshness",
|
|
229
|
+
"ExposableMarket",
|
|
230
|
+
"ExposableSetLang",
|
|
231
|
+
]
|
unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/vertexai/schema.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, ClassVar, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import Field, field_validator
|
|
6
|
+
from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
7
|
+
|
|
8
|
+
from unique_search_proxy_core.agent_engines.base import (
|
|
9
|
+
AgentEngineType,
|
|
10
|
+
BaseAgentEngineConfig,
|
|
11
|
+
)
|
|
12
|
+
from unique_search_proxy_core.agent_engines.vertexai.settings import (
|
|
13
|
+
resolve_enable_enterprise_search,
|
|
14
|
+
vertex_ai_env_settings,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_force_activate_enterprise_search_description() -> str:
|
|
19
|
+
if vertex_ai_env_settings.force_activate_enterprise_search:
|
|
20
|
+
return "This parameter has been enforced by infra team."
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
"When true, ground answers with Vertex AI Enterprise Web Search "
|
|
24
|
+
"(Agent Platform Search, SEC4-compliant)"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _enterprise_search_rjsf_tag() -> RJSFMetaTag:
|
|
29
|
+
return RJSFMetaTag.BooleanWidget.checkbox(
|
|
30
|
+
title="Enable Enterprise Search",
|
|
31
|
+
help=_get_force_activate_enterprise_search_description(),
|
|
32
|
+
disabled=vertex_ai_env_settings.force_activate_enterprise_search,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
EnableEnterpriseSearch = Annotated[bool, _enterprise_search_rjsf_tag()]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class VertexAIAgentConfig(BaseAgentEngineConfig[Literal[AgentEngineType.VERTEXAI]]):
|
|
40
|
+
"""Deployment + request defaults for Vertex AI grounding (Google GenAI)."""
|
|
41
|
+
|
|
42
|
+
_request_model_name: ClassVar[str] = "VertexAIAgentSearchRequest"
|
|
43
|
+
_exposed_params_model_name: ClassVar[str] = "VertexAIAgentExposedParams"
|
|
44
|
+
|
|
45
|
+
engine: Annotated[
|
|
46
|
+
Literal[AgentEngineType.VERTEXAI], RJSFMetaTag.SpecialWidget.hidden()
|
|
47
|
+
] = Field(
|
|
48
|
+
default=AgentEngineType.VERTEXAI,
|
|
49
|
+
title="Agent engine",
|
|
50
|
+
description="Provider discriminator; must be `vertexai` for this config.",
|
|
51
|
+
)
|
|
52
|
+
vertexai_model_name: str = Field(
|
|
53
|
+
default=vertex_ai_env_settings.default_vertex_ai_model_name,
|
|
54
|
+
description="Gemini model name for grounded generation",
|
|
55
|
+
)
|
|
56
|
+
enable_enterprise_search: EnableEnterpriseSearch = Field(
|
|
57
|
+
default=vertex_ai_env_settings.force_activate_enterprise_search,
|
|
58
|
+
validate_default=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@field_validator("enable_enterprise_search", mode="before")
|
|
62
|
+
@classmethod
|
|
63
|
+
def validate_enable_enterprise_search(cls, v: object) -> bool:
|
|
64
|
+
"""Pin the value on when infra forces enterprise search for ZDR tenants."""
|
|
65
|
+
return resolve_enable_enterprise_search(bool(v) if v is not None else False)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
VertexAIAgentSearchRequest = VertexAIAgentConfig.request_model()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
"VertexAIAgentConfig",
|
|
73
|
+
"VertexAIAgentSearchRequest",
|
|
74
|
+
]
|
unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/agent_engines/vertexai/settings.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
|
|
8
|
+
_ENV_PREFIX = "VERTEXAI_AGENT_"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _VertexAIEnvSettings(BaseSettings):
|
|
12
|
+
default_vertex_ai_model_name: str = Field(
|
|
13
|
+
default="gemini-3-flash-preview",
|
|
14
|
+
description="Default Vertex AI model name for grounded generation",
|
|
15
|
+
)
|
|
16
|
+
force_activate_enterprise_search: bool = Field(
|
|
17
|
+
default=False,
|
|
18
|
+
description=(
|
|
19
|
+
"When true, pin Enable Enterprise Search on for ZDR tenants so the "
|
|
20
|
+
"toggle cannot be turned off in config or at request time."
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_settings() -> _VertexAIEnvSettings:
|
|
26
|
+
if "pytest" in sys.modules:
|
|
27
|
+
env_file = Path(os.getcwd()) / "tests/test.env"
|
|
28
|
+
else:
|
|
29
|
+
env_file = Path(os.getcwd()) / ".env"
|
|
30
|
+
|
|
31
|
+
class _Settings(_VertexAIEnvSettings):
|
|
32
|
+
model_config = SettingsConfigDict(
|
|
33
|
+
env_file=env_file,
|
|
34
|
+
env_prefix=_ENV_PREFIX,
|
|
35
|
+
extra="ignore",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return _Settings()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
vertex_ai_env_settings = _get_settings()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def resolve_enable_enterprise_search(value: bool | None = False) -> bool:
|
|
45
|
+
"""Return the effective enterprise-search flag after infra enforcement.
|
|
46
|
+
|
|
47
|
+
ZDR tenants set ``VERTEXAI_AGENT_FORCE_ACTIVATE_ENTERPRISE_SEARCH=true`` so
|
|
48
|
+
the cheaper standard grounding edition cannot be selected.
|
|
49
|
+
"""
|
|
50
|
+
if vertex_ai_env_settings.force_activate_enterprise_search:
|
|
51
|
+
return True
|
|
52
|
+
return bool(value)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Model factories: deployment config class -> derived Pydantic models.
|
|
2
2
|
|
|
3
3
|
Private-by-convention; the public entry points are the config-class methods
|
|
4
|
-
(``
|
|
5
|
-
|
|
4
|
+
(``request_model()`` / ``exposed_params_model()`` on the search and agent bases
|
|
5
|
+
through :class:`~unique_search_proxy_core.param_policy.exposable_config.ExposableParamsConfig`,
|
|
6
|
+
and ``request_model()`` on the crawler base), which delegate here.
|
|
6
7
|
|
|
7
8
|
Two factories, both plain ``create_model`` calls over ``model_fields``:
|
|
8
9
|
|
unique_search_proxy_core-2026.36.0.dev4/unique_search_proxy_core/param_policy/exposable_config.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Config base for engines carrying ``ExposableParam`` knobs.
|
|
2
|
+
|
|
3
|
+
Standard search engines and agent engines own the same parameter lifecycle:
|
|
4
|
+
partial exposable JSON is merged with the field's ``default_factory`` before
|
|
5
|
+
validation, the admin-exposed subset is projected into an LLM-facing model, and
|
|
6
|
+
deployment defaults are resolved into a validated request body. That lifecycle
|
|
7
|
+
lives here so :class:`~unique_search_proxy_core.search_engines.base.BaseSearchEngineConfig`
|
|
8
|
+
and :class:`~unique_search_proxy_core.agent_engines.base.BaseAgentEngineConfig`
|
|
9
|
+
share one implementation.
|
|
10
|
+
|
|
11
|
+
Subclasses supply the two pieces this base cannot know: ``request_model()``
|
|
12
|
+
(which request base class the derived body extends) and the
|
|
13
|
+
``_exposed_params_model_name`` / ``_merge_exclude_fields`` class vars.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any, ClassVar, Mapping
|
|
19
|
+
|
|
20
|
+
from pydantic import BaseModel, model_validator
|
|
21
|
+
|
|
22
|
+
from unique_search_proxy_core.param_policy import QUERY_FIELD
|
|
23
|
+
from unique_search_proxy_core.param_policy.derive import derive_exposed_params_model
|
|
24
|
+
from unique_search_proxy_core.param_policy.exposable_param import (
|
|
25
|
+
merge_exposable_params_with_factory_defaults,
|
|
26
|
+
resolve_exposable_value,
|
|
27
|
+
)
|
|
28
|
+
from unique_search_proxy_core.param_policy.exposed_params import ExposedParams
|
|
29
|
+
from unique_search_proxy_core.schema import camelized_model_config
|
|
30
|
+
|
|
31
|
+
#: Provider discriminator field carried by every engine config.
|
|
32
|
+
ENGINE_FIELD = "engine"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ExposableParamsConfig(BaseModel):
|
|
36
|
+
"""Deployment config whose optional knobs may be exposed to the LLM.
|
|
37
|
+
|
|
38
|
+
- :meth:`exposed_params_model` — the LLM-facing knobs of this deployment.
|
|
39
|
+
- :meth:`merge` — deployment defaults + LLM overrides -> validated request.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
model_config = camelized_model_config
|
|
43
|
+
|
|
44
|
+
#: Name of the derived exposed-params model; set by every concrete config.
|
|
45
|
+
_exposed_params_model_name: ClassVar[str]
|
|
46
|
+
|
|
47
|
+
#: Config fields that never contribute a request default in :meth:`merge`.
|
|
48
|
+
#: The discriminator is set from the config itself; engine bases extend this
|
|
49
|
+
#: with their own server-side-only fields (e.g. the agent ``output_schema``).
|
|
50
|
+
_merge_exclude_fields: ClassVar[frozenset[str]] = frozenset({ENGINE_FIELD})
|
|
51
|
+
|
|
52
|
+
@model_validator(mode="before")
|
|
53
|
+
@classmethod
|
|
54
|
+
def _merge_exposable_factory_defaults(cls, data: Any) -> Any:
|
|
55
|
+
"""Merge exposable knobs with field ``default_factory`` when JSON omits ``value``."""
|
|
56
|
+
return merge_exposable_params_with_factory_defaults(cls, data)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def request_model(cls) -> type[BaseModel]:
|
|
60
|
+
"""HTTP request body model derived from this config."""
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
|
|
63
|
+
def exposed_params_model(self) -> type[ExposedParams] | None:
|
|
64
|
+
"""Plain model class with exactly the knobs this deployment exposed.
|
|
65
|
+
|
|
66
|
+
One optional field per config field whose ``ExposableParam`` has
|
|
67
|
+
``expose=True``: camelCase alias, description-only schema, no admin
|
|
68
|
+
defaults. Returns ``None`` when nothing is exposed. Consumers subclass
|
|
69
|
+
the result (or pass it as an extra base to ``create_model``) to graft
|
|
70
|
+
the knobs onto their own tool-parameter models.
|
|
71
|
+
|
|
72
|
+
Example: ``config.exposed_params_model()`` -> ``GoogleExposedParams``.
|
|
73
|
+
"""
|
|
74
|
+
return derive_exposed_params_model(
|
|
75
|
+
self,
|
|
76
|
+
name=type(self)._exposed_params_model_name,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def merge(self, overrides: Mapping[str, Any], *, query: str) -> BaseModel:
|
|
80
|
+
"""Deployment defaults + LLM/caller overrides + query -> validated request.
|
|
81
|
+
|
|
82
|
+
Defaults: plain fields contribute their value; ``ExposableParam`` knobs
|
|
83
|
+
contribute ``value`` when not ``None`` (deactivated knobs are dropped);
|
|
84
|
+
``engine`` always comes from this config. ``overrides`` win over
|
|
85
|
+
defaults. Validates into :meth:`request_model`.
|
|
86
|
+
|
|
87
|
+
Example: ``config.merge({"date_restrict": "d7"}, query="ai news")``.
|
|
88
|
+
"""
|
|
89
|
+
merged: dict[str, Any] = self._merge_defaults()
|
|
90
|
+
merged.update(overrides)
|
|
91
|
+
merged[QUERY_FIELD] = query
|
|
92
|
+
merged[ENGINE_FIELD] = getattr(self, ENGINE_FIELD)
|
|
93
|
+
return type(self).request_model().model_validate(merged)
|
|
94
|
+
|
|
95
|
+
def _merge_defaults(self) -> dict[str, Any]:
|
|
96
|
+
"""Plain deployment default values (``ExposableParam`` resolved, ``None`` dropped)."""
|
|
97
|
+
defaults: dict[str, Any] = {}
|
|
98
|
+
for field_name in type(self).model_fields:
|
|
99
|
+
if field_name in type(self)._merge_exclude_fields:
|
|
100
|
+
continue
|
|
101
|
+
value = resolve_exposable_value(getattr(self, field_name))
|
|
102
|
+
if value is not None:
|
|
103
|
+
defaults[field_name] = value
|
|
104
|
+
return defaults
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
__all__ = ["ENGINE_FIELD", "ExposableParamsConfig"]
|
|
@@ -121,6 +121,17 @@ class ExposableParam(BaseModel, Generic[T]):
|
|
|
121
121
|
return schema
|
|
122
122
|
|
|
123
123
|
|
|
124
|
+
def resolve_exposable_value(value: Any) -> Any:
|
|
125
|
+
"""Plain value behind a config attribute that may be an ``ExposableParam``.
|
|
126
|
+
|
|
127
|
+
Lets consumers read a deployment default without caring whether the field is
|
|
128
|
+
an exposable knob or a plain value.
|
|
129
|
+
"""
|
|
130
|
+
if isinstance(value, ExposableParam):
|
|
131
|
+
return value.value
|
|
132
|
+
return value
|
|
133
|
+
|
|
134
|
+
|
|
124
135
|
def _exposable_field_keys(field_name: str, field_info: FieldInfo) -> list[str]:
|
|
125
136
|
keys = [field_name]
|
|
126
137
|
if field_info.alias is not None:
|
|
@@ -2,26 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from enum import StrEnum
|
|
5
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Generic,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
from unique_search_proxy_core.param_policy import QUERY_FIELD
|
|
10
|
-
from unique_search_proxy_core.param_policy.derive import
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
from unique_search_proxy_core.param_policy.derive import derive_request_model
|
|
11
|
+
from unique_search_proxy_core.param_policy.exposable_config import (
|
|
12
|
+
ENGINE_FIELD,
|
|
13
|
+
ExposableParamsConfig,
|
|
13
14
|
)
|
|
14
|
-
from unique_search_proxy_core.param_policy.exposable_param import (
|
|
15
|
-
ExposableParam,
|
|
16
|
-
merge_exposable_params_with_factory_defaults,
|
|
17
|
-
)
|
|
18
|
-
from unique_search_proxy_core.param_policy.exposed_params import ExposedParams
|
|
19
15
|
from unique_search_proxy_core.param_policy.request_base import SearchRequestBase
|
|
20
|
-
from unique_search_proxy_core.schema import
|
|
21
|
-
SearchEngineRaw,
|
|
22
|
-
WebSearchResults,
|
|
23
|
-
camelized_model_config,
|
|
24
|
-
)
|
|
16
|
+
from unique_search_proxy_core.schema import SearchEngineRaw, WebSearchResults
|
|
25
17
|
|
|
26
18
|
if TYPE_CHECKING:
|
|
27
19
|
from httpx import AsyncClient
|
|
@@ -29,7 +21,6 @@ if TYPE_CHECKING:
|
|
|
29
21
|
T = TypeVar("T", bound="SearchEngineType")
|
|
30
22
|
SearchRequestT = TypeVar("SearchRequestT", bound=BaseModel)
|
|
31
23
|
|
|
32
|
-
ENGINE_FIELD = "engine"
|
|
33
24
|
FETCH_SIZE_FIELD = "fetch_size"
|
|
34
25
|
TIMEOUT_FIELD = "timeout"
|
|
35
26
|
|
|
@@ -64,7 +55,7 @@ def get_search_engine_mode(
|
|
|
64
55
|
return _SEARCH_ENGINE_MODE_MAP.get(engine_type, SearchEngineMode.STANDARD)
|
|
65
56
|
|
|
66
57
|
|
|
67
|
-
class BaseSearchEngineConfig(
|
|
58
|
+
class BaseSearchEngineConfig(ExposableParamsConfig, Generic[T]):
|
|
68
59
|
"""Shared search-engine config; each engine narrows ``engine`` with a Literal.
|
|
69
60
|
|
|
70
61
|
The config class owns its entire parameter lifecycle:
|
|
@@ -75,8 +66,6 @@ class BaseSearchEngineConfig(BaseModel, Generic[T]):
|
|
|
75
66
|
- :meth:`provider_query_params` — request -> upstream provider dict.
|
|
76
67
|
"""
|
|
77
68
|
|
|
78
|
-
model_config = camelized_model_config
|
|
79
|
-
|
|
80
69
|
#: Fields never forwarded to the upstream provider query string: the
|
|
81
70
|
#: discriminator, request plumbing, and the query itself. Engine configs
|
|
82
71
|
#: extend this to drop provider-internal fields — e.g. Google adds
|
|
@@ -85,9 +74,8 @@ class BaseSearchEngineConfig(BaseModel, Generic[T]):
|
|
|
85
74
|
{ENGINE_FIELD, FETCH_SIZE_FIELD, TIMEOUT_FIELD, QUERY_FIELD},
|
|
86
75
|
)
|
|
87
76
|
|
|
88
|
-
#:
|
|
77
|
+
#: Name of the derived request model; set by every concrete engine config.
|
|
89
78
|
_request_model_name: ClassVar[str]
|
|
90
|
-
_exposed_params_model_name: ClassVar[str]
|
|
91
79
|
|
|
92
80
|
engine: T
|
|
93
81
|
fetch_size: int = Field(
|
|
@@ -103,12 +91,6 @@ class BaseSearchEngineConfig(BaseModel, Generic[T]):
|
|
|
103
91
|
description="Request timeout in seconds",
|
|
104
92
|
)
|
|
105
93
|
|
|
106
|
-
@model_validator(mode="before")
|
|
107
|
-
@classmethod
|
|
108
|
-
def _merge_exposable_factory_defaults(cls, data: Any) -> Any:
|
|
109
|
-
"""Merge exposable knobs with field ``default_factory`` when JSON omits ``value``."""
|
|
110
|
-
return merge_exposable_params_with_factory_defaults(cls, data)
|
|
111
|
-
|
|
112
94
|
@classmethod
|
|
113
95
|
def request_model(cls) -> type[BaseModel]:
|
|
114
96
|
"""HTTP request body model, cached per config class.
|
|
@@ -124,52 +106,6 @@ class BaseSearchEngineConfig(BaseModel, Generic[T]):
|
|
|
124
106
|
name=cls._request_model_name,
|
|
125
107
|
)
|
|
126
108
|
|
|
127
|
-
def exposed_params_model(self) -> type[ExposedParams] | None:
|
|
128
|
-
"""Plain model class with exactly the knobs this deployment exposed.
|
|
129
|
-
|
|
130
|
-
One optional field per config field whose ``ExposableParam`` has
|
|
131
|
-
``expose=True``: camelCase alias, description-only schema, no admin
|
|
132
|
-
defaults. Returns ``None`` when nothing is exposed. Consumers subclass
|
|
133
|
-
the result (or pass it as an extra base to ``create_model``) to graft
|
|
134
|
-
the knobs onto their own tool-parameter models.
|
|
135
|
-
|
|
136
|
-
Example: ``config.exposed_params_model()`` -> ``GoogleExposedParams``.
|
|
137
|
-
"""
|
|
138
|
-
return derive_exposed_params_model(
|
|
139
|
-
self,
|
|
140
|
-
name=type(self)._exposed_params_model_name,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
def merge(self, overrides: Mapping[str, Any], *, query: str) -> BaseModel:
|
|
144
|
-
"""Deployment defaults + LLM/caller overrides + query -> validated request.
|
|
145
|
-
|
|
146
|
-
Defaults: plain fields contribute their value; ``ExposableParam`` knobs
|
|
147
|
-
contribute ``value`` when not ``None`` (deactivated knobs are dropped);
|
|
148
|
-
``engine`` always comes from this config. ``overrides`` win over
|
|
149
|
-
defaults. Validates into :meth:`request_model`.
|
|
150
|
-
|
|
151
|
-
Example: ``config.merge({"date_restrict": "d7"}, query="ai news")``.
|
|
152
|
-
"""
|
|
153
|
-
merged: dict[str, Any] = self._merge_defaults()
|
|
154
|
-
merged.update(overrides)
|
|
155
|
-
merged[QUERY_FIELD] = query
|
|
156
|
-
merged[ENGINE_FIELD] = self.engine
|
|
157
|
-
return type(self).request_model().model_validate(merged)
|
|
158
|
-
|
|
159
|
-
def _merge_defaults(self) -> dict[str, Any]:
|
|
160
|
-
"""Plain deployment default values (``ExposableParam`` resolved, ``None`` dropped)."""
|
|
161
|
-
defaults: dict[str, Any] = {}
|
|
162
|
-
for field_name in type(self).model_fields:
|
|
163
|
-
if field_name == ENGINE_FIELD:
|
|
164
|
-
continue
|
|
165
|
-
raw = getattr(self, field_name)
|
|
166
|
-
if isinstance(raw, ExposableParam):
|
|
167
|
-
if raw.value is not None:
|
|
168
|
-
defaults[field_name] = raw.value
|
|
169
|
-
elif raw is not None:
|
|
170
|
-
defaults[field_name] = raw
|
|
171
|
-
return defaults
|
|
172
|
-
|
|
173
109
|
@classmethod
|
|
174
110
|
def provider_query_params(
|
|
175
111
|
cls,
|
unique_search_proxy_core-2026.36.0.dev2/unique_search_proxy_core/agent_engines/bing/schema.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Annotated, ClassVar, Literal
|
|
4
|
-
|
|
5
|
-
from pydantic import Field
|
|
6
|
-
from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
7
|
-
|
|
8
|
-
from unique_search_proxy_core.agent_engines.base import (
|
|
9
|
-
AgentEngineType,
|
|
10
|
-
BaseAgentEngineConfig,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class BingAgentConfig(BaseAgentEngineConfig[Literal[AgentEngineType.BING]]):
|
|
15
|
-
"""Deployment + request defaults for Bing grounding via Azure AI Projects."""
|
|
16
|
-
|
|
17
|
-
_request_model_name: ClassVar[str] = "BingAgentSearchRequest"
|
|
18
|
-
|
|
19
|
-
engine: Annotated[
|
|
20
|
-
Literal[AgentEngineType.BING], RJSFMetaTag.SpecialWidget.hidden()
|
|
21
|
-
] = Field(
|
|
22
|
-
default=AgentEngineType.BING,
|
|
23
|
-
title="Agent engine",
|
|
24
|
-
description="Provider discriminator; must be `bing` for this config.",
|
|
25
|
-
)
|
|
26
|
-
fetch_size: int = Field(
|
|
27
|
-
default=5,
|
|
28
|
-
ge=1,
|
|
29
|
-
le=50,
|
|
30
|
-
description="Maximum number of Bing grounding results per query",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
BingAgentSearchRequest = BingAgentConfig.request_model()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
__all__ = ["BingAgentConfig", "BingAgentSearchRequest"]
|
unique_search_proxy_core-2026.36.0.dev2/unique_search_proxy_core/agent_engines/vertexai/schema.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Annotated, ClassVar, Literal
|
|
4
|
-
|
|
5
|
-
from pydantic import Field
|
|
6
|
-
from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
7
|
-
|
|
8
|
-
from unique_search_proxy_core.agent_engines.base import (
|
|
9
|
-
AgentEngineType,
|
|
10
|
-
BaseAgentEngineConfig,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class VertexAIAgentConfig(BaseAgentEngineConfig[Literal[AgentEngineType.VERTEXAI]]):
|
|
15
|
-
"""Deployment + request defaults for Vertex AI grounding (Google GenAI)."""
|
|
16
|
-
|
|
17
|
-
_request_model_name: ClassVar[str] = "VertexAIAgentSearchRequest"
|
|
18
|
-
|
|
19
|
-
engine: Annotated[
|
|
20
|
-
Literal[AgentEngineType.VERTEXAI], RJSFMetaTag.SpecialWidget.hidden()
|
|
21
|
-
] = Field(
|
|
22
|
-
default=AgentEngineType.VERTEXAI,
|
|
23
|
-
title="Agent engine",
|
|
24
|
-
description="Provider discriminator; must be `vertexai` for this config.",
|
|
25
|
-
)
|
|
26
|
-
vertexai_model_name: str = Field(
|
|
27
|
-
default="gemini-3-flash-preview",
|
|
28
|
-
description="Gemini model name for grounded generation",
|
|
29
|
-
)
|
|
30
|
-
enable_enterprise_search: bool = Field(
|
|
31
|
-
default=False,
|
|
32
|
-
description=(
|
|
33
|
-
"When true, ground answers with Vertex AI Enterprise Web Search "
|
|
34
|
-
"(Agent Platform Search, SEC4-compliant)"
|
|
35
|
-
),
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
VertexAIAgentSearchRequest = VertexAIAgentConfig.request_model()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
__all__ = [
|
|
43
|
-
"VertexAIAgentConfig",
|
|
44
|
-
"VertexAIAgentSearchRequest",
|
|
45
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|