unique-search-proxy 2026.42.0.dev0__tar.gz → 2026.42.0.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/PKG-INFO +11 -3
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/README.md +8 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/pyproject.toml +3 -3
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/service.py +19 -9
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/monitoring/metrics.py +12 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/monitoring/setup.py +6 -1
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/health.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/agent_search.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/configuration.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/context_headers.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/crawl.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/openapi_examples.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/api/v1/search.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/app.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/context.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/bing/cleanup.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/bing/client.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/bing/runner.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/bing/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/factory.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/service_base.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/structured_output.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/vertexai/client.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/vertexai/gemini.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/agent_engines/vertexai/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/client/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/client/credentials.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/client/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/errors.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/html_markdown.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/html.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/pdf.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/plain_text.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/processing/registry.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/settings.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/basic/user_agent.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/factory.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/firecrawl/polling.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/firecrawl/request_body.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/firecrawl/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/jina/request_body.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/jina/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/pinned_egress.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/tavily/request_body.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/crawlers/tavily/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/provider_response.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/providers.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/registry.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/brave/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/brave/pagination.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/brave/query_params.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/brave/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/descriptor.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/factory.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/google/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/google/pagination.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/google/query_params.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/google/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/pagination.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/perplexity/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/perplexity/request_body.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/perplexity/service.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/search_engines/service_base.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/url_safety/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/core/url_safety/gate.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/error_handlers.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/generator/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/generator/introspect.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/generator/schema.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/generator/template.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/generator/templates/provider_values_yaml.j2 +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/generator/values_yaml.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/metadata.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/registry.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/helm/url_safety_registration.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/logging_config.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/middleware/context.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/monitoring/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/presets/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/presets/common.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/presets/crawl.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/presets/search.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/presets/types.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/app.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/base.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/client.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/monitoring.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/base.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/bing_agent.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/brave.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/firecrawl.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/google.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/jina.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/perplexity.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/tavily.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/providers/vertexai_agent.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/settings/secret_str.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/startup_report.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/utils/__init__.py +0 -0
- {unique_search_proxy-2026.42.0.dev0 → unique_search_proxy-2026.42.0.dev2}/unique_search_proxy_client/web/utils/url.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-search-proxy
|
|
3
|
-
Version: 2026.42.0.
|
|
3
|
+
Version: 2026.42.0.dev2
|
|
4
4
|
Summary: Web Search Proxy implementation
|
|
5
5
|
Author: ThePhilAz
|
|
6
6
|
Author-email: ThePhilAz <rami.azouz@philico.com>
|
|
@@ -19,8 +19,8 @@ Requires-Dist: certifi>=2025.11.12,<2027
|
|
|
19
19
|
Requires-Dist: google-genai>=1.73.0,<2
|
|
20
20
|
Requires-Dist: google-auth>=2.43.0,<3
|
|
21
21
|
Requires-Dist: jinja2>=3.1.6,<4
|
|
22
|
-
Requires-Dist: unique-toolkit[monitoring]>=2026.42.0.
|
|
23
|
-
Requires-Dist: unique-search-proxy-core>=2026.42.0.
|
|
22
|
+
Requires-Dist: unique-toolkit[monitoring]>=2026.42.0.dev13,<2026.42.0rc0
|
|
23
|
+
Requires-Dist: unique-search-proxy-core>=2026.42.0.dev2,<2026.42.0rc0
|
|
24
24
|
Requires-Dist: openai>=1.105.0,<3
|
|
25
25
|
Requires-Python: >=3.12
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
@@ -237,8 +237,16 @@ Settings use pydantic-settings with per-provider env vars. Copy `.env.example` t
|
|
|
237
237
|
| VertexAI agent | `VERTEXAI_AGENT_` | `VERTEXAI_AGENT_SERVICE_ACCOUNT_CREDENTIALS` (optional) |
|
|
238
238
|
| HTTP client | `HTTP_CLIENT_` | `HTTP_CLIENT_PROXY_HOST`, `HTTP_CLIENT_POOL_TIMEOUT_SECONDS` |
|
|
239
239
|
| Prometheus | `PROMETHEUS_` | `PROMETHEUS_ENABLED` |
|
|
240
|
+
| URL safety | `URL_SAFETY_` | `URL_SAFETY_ENABLED` |
|
|
240
241
|
| Container | (shell) | `HOST`, `PORT`, `WORKERS`, `LOG_LEVEL` |
|
|
241
242
|
|
|
243
|
+
`URL_SAFETY_ENABLED` defaults to `true` when unset. Setting it to `false`
|
|
244
|
+
disables application-level SSRF checks and is accepted only when every request
|
|
245
|
+
uses a configured authenticated corporate proxy; incompatible direct or partial
|
|
246
|
+
proxy routing fails during startup. The application cannot verify that the
|
|
247
|
+
proxy implements equivalent URL-safety controls, so this state is highlighted
|
|
248
|
+
in the Prometheus metric and Grafana dashboard.
|
|
249
|
+
|
|
242
250
|
With `WORKERS > 1`, the entrypoint sets `PROMETHEUS_MULTIPROC_DIR` for correct metric aggregation.
|
|
243
251
|
|
|
244
252
|
---
|
|
@@ -210,8 +210,16 @@ Settings use pydantic-settings with per-provider env vars. Copy `.env.example` t
|
|
|
210
210
|
| VertexAI agent | `VERTEXAI_AGENT_` | `VERTEXAI_AGENT_SERVICE_ACCOUNT_CREDENTIALS` (optional) |
|
|
211
211
|
| HTTP client | `HTTP_CLIENT_` | `HTTP_CLIENT_PROXY_HOST`, `HTTP_CLIENT_POOL_TIMEOUT_SECONDS` |
|
|
212
212
|
| Prometheus | `PROMETHEUS_` | `PROMETHEUS_ENABLED` |
|
|
213
|
+
| URL safety | `URL_SAFETY_` | `URL_SAFETY_ENABLED` |
|
|
213
214
|
| Container | (shell) | `HOST`, `PORT`, `WORKERS`, `LOG_LEVEL` |
|
|
214
215
|
|
|
216
|
+
`URL_SAFETY_ENABLED` defaults to `true` when unset. Setting it to `false`
|
|
217
|
+
disables application-level SSRF checks and is accepted only when every request
|
|
218
|
+
uses a configured authenticated corporate proxy; incompatible direct or partial
|
|
219
|
+
proxy routing fails during startup. The application cannot verify that the
|
|
220
|
+
proxy implements equivalent URL-safety controls, so this state is highlighted
|
|
221
|
+
in the Prometheus metric and Grafana dashboard.
|
|
222
|
+
|
|
215
223
|
With `WORKERS > 1`, the entrypoint sets `PROMETHEUS_MULTIPROC_DIR` for correct metric aggregation.
|
|
216
224
|
|
|
217
225
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique-search-proxy"
|
|
3
|
-
version = "2026.42.0.
|
|
3
|
+
version = "2026.42.0.dev2"
|
|
4
4
|
description = "Web Search Proxy implementation"
|
|
5
5
|
authors = [{ name = "ThePhilAz", email = "rami.azouz@philico.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -21,8 +21,8 @@ dependencies = [
|
|
|
21
21
|
"google-genai>=1.73.0,<2",
|
|
22
22
|
"google-auth>=2.43.0,<3",
|
|
23
23
|
"jinja2>=3.1.6,<4",
|
|
24
|
-
"unique-toolkit[monitoring]>=2026.42.0.
|
|
25
|
-
"unique-search-proxy-core>=2026.42.0.
|
|
24
|
+
"unique-toolkit[monitoring]>=2026.42.0.dev13,<2026.42.0rc0",
|
|
25
|
+
"unique-search-proxy-core>=2026.42.0.dev2,<2026.42.0rc0",
|
|
26
26
|
"openai>=1.105.0,<3",
|
|
27
27
|
]
|
|
28
28
|
|
|
@@ -15,9 +15,11 @@ from unique_search_proxy_core.schema import (
|
|
|
15
15
|
ProxyErrorCode,
|
|
16
16
|
)
|
|
17
17
|
from unique_search_proxy_core.url_safety import (
|
|
18
|
+
CrawlTargetValidationError,
|
|
18
19
|
ResolvedCrawlTarget,
|
|
19
20
|
bypass_crawl_target,
|
|
20
|
-
|
|
21
|
+
safe_pinned_httpx_get,
|
|
22
|
+
url_safety_settings,
|
|
21
23
|
)
|
|
22
24
|
|
|
23
25
|
from unique_search_proxy_client.web.core.crawlers.basic.processing import (
|
|
@@ -29,10 +31,12 @@ from unique_search_proxy_client.web.core.crawlers.basic.user_agent import (
|
|
|
29
31
|
random_user_agent,
|
|
30
32
|
)
|
|
31
33
|
from unique_search_proxy_client.web.core.provider_response import (
|
|
34
|
+
crawl_forbidden_target,
|
|
32
35
|
crawl_upstream_error,
|
|
33
36
|
transport_error_raw,
|
|
34
37
|
)
|
|
35
38
|
from unique_search_proxy_client.web.core.url_safety.gate import AllowedCrawlTarget
|
|
39
|
+
from unique_search_proxy_client.web.monitoring.metrics import record_crawl_blocked
|
|
36
40
|
|
|
37
41
|
_LOGGER = logging.getLogger(__name__)
|
|
38
42
|
|
|
@@ -101,18 +105,24 @@ class BasicCrawlerService(BaseCrawler[BasicCrawlRequest]):
|
|
|
101
105
|
semaphore: asyncio.Semaphore,
|
|
102
106
|
content_type_handlers: dict[str, ContentTypeHandlerPolicy],
|
|
103
107
|
) -> CrawlUrlResult:
|
|
104
|
-
request_url, pin_headers, extensions = pinned_httpx_get_args(resolved_target)
|
|
105
108
|
async with semaphore:
|
|
106
|
-
headers = {"User-Agent": random_user_agent(), **pin_headers}
|
|
107
|
-
|
|
108
109
|
try:
|
|
109
|
-
response = await
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
response = await safe_pinned_httpx_get(
|
|
111
|
+
client,
|
|
112
|
+
resolved_target,
|
|
113
|
+
headers={"User-Agent": random_user_agent()},
|
|
113
114
|
timeout=Timeout(timeout),
|
|
114
|
-
|
|
115
|
+
max_redirect_hops=url_safety_settings.max_redirect_hops,
|
|
116
|
+
enforce_url_safety=url_safety_settings.enabled,
|
|
117
|
+
)
|
|
118
|
+
except CrawlTargetValidationError as exc:
|
|
119
|
+
blocked = exc.blocked_targets[0]
|
|
120
|
+
record_crawl_blocked(blocked.category)
|
|
121
|
+
_LOGGER.warning(
|
|
122
|
+
"Basic crawl blocked redirect: %s",
|
|
123
|
+
blocked.category,
|
|
115
124
|
)
|
|
125
|
+
return crawl_forbidden_target(display_url, blocked.reason)
|
|
116
126
|
except httpx.TimeoutException as exc:
|
|
117
127
|
_LOGGER.warning("Basic crawl timed out for %s: %s", display_url, exc)
|
|
118
128
|
return crawl_upstream_error(
|
|
@@ -93,6 +93,12 @@ crawl_blocked_total = m.counter(
|
|
|
93
93
|
"Crawl targets blocked by URL safety policy",
|
|
94
94
|
["reason_category"],
|
|
95
95
|
)
|
|
96
|
+
url_safety_application_checks_enabled = m.gauge(
|
|
97
|
+
"url_safety_application_checks_enabled",
|
|
98
|
+
"Whether application-level URL safety checks are enabled (1) or delegated "
|
|
99
|
+
"to a configured corporate proxy (0)",
|
|
100
|
+
[],
|
|
101
|
+
)
|
|
96
102
|
|
|
97
103
|
agent_search_duration_seconds = m.histogram(
|
|
98
104
|
"agent_search_duration_seconds",
|
|
@@ -182,6 +188,12 @@ def record_crawl_blocked(reason_category: str, count: int = 1) -> None:
|
|
|
182
188
|
crawl_blocked_total.labels(reason_category=reason_category).inc(count)
|
|
183
189
|
|
|
184
190
|
|
|
191
|
+
def set_url_safety_application_checks_enabled(enabled: bool) -> None:
|
|
192
|
+
if not _metrics_enabled():
|
|
193
|
+
return
|
|
194
|
+
url_safety_application_checks_enabled.set(int(enabled))
|
|
195
|
+
|
|
196
|
+
|
|
185
197
|
def record_agent_search_success(engine: str, duration_seconds: float) -> None:
|
|
186
198
|
if not _metrics_enabled():
|
|
187
199
|
return
|
|
@@ -4,9 +4,13 @@ import logging
|
|
|
4
4
|
|
|
5
5
|
from fastapi import FastAPI
|
|
6
6
|
from fastapi.responses import PlainTextResponse
|
|
7
|
+
from unique_search_proxy_core.url_safety import url_safety_settings
|
|
7
8
|
|
|
8
9
|
import unique_search_proxy_client.web.settings.monitoring as monitoring_settings
|
|
9
|
-
from unique_search_proxy_client.web.monitoring.metrics import
|
|
10
|
+
from unique_search_proxy_client.web.monitoring.metrics import (
|
|
11
|
+
HTTP_LATENCY_BUCKETS,
|
|
12
|
+
set_url_safety_application_checks_enabled,
|
|
13
|
+
)
|
|
10
14
|
|
|
11
15
|
_LOGGER = logging.getLogger(__name__)
|
|
12
16
|
|
|
@@ -50,5 +54,6 @@ def setup_prometheus(app: FastAPI) -> bool:
|
|
|
50
54
|
media_type="text/plain; version=0.0.4; charset=utf-8",
|
|
51
55
|
)
|
|
52
56
|
|
|
57
|
+
set_url_safety_application_checks_enabled(url_safety_settings.enabled)
|
|
53
58
|
_LOGGER.info("Prometheus metrics enabled at /metrics")
|
|
54
59
|
return True
|
|
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
|
|
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
|
|
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
|