unique-search-proxy 2026.44.0.dev9__tar.gz → 2026.44.0.dev11__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.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/PKG-INFO +2 -2
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/pyproject.toml +2 -2
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/crawl.py +20 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/service.py +15 -1
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/registry.py +4 -0
- unique_search_proxy-2026.44.0.dev11/unique_search_proxy_client/web/settings/request_limits.py +79 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/README.md +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/health.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/agent_search.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/configuration.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/context_headers.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/openapi_examples.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/api/v1/search.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/app.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/context.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/cleanup.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/client.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/runner.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/bing/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/factory.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/service_base.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/structured_output.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/client.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/gemini.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/redirects.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/agent_engines/vertexai/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/azure_transport.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/credentials.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/client/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/errors.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/html_markdown.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/html.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/pdf.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/processors/plain_text.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/processing/registry.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/settings.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/basic/user_agent.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/factory.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/firecrawl/polling.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/firecrawl/request_body.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/firecrawl/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/jina/request_body.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/jina/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/pinned_egress.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/tavily/request_body.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/crawlers/tavily/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/provider_response.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/providers.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/registry.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/pagination.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/query_params.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/brave/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/custom_api/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/custom_api/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/descriptor.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/factory.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/pagination.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/query_params.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/google/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/pagination.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/perplexity/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/perplexity/request_body.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/perplexity/service.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/search_engines/service_base.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/url_safety/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/core/url_safety/gate.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/error_handlers.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/introspect.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/schema.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/template.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/templates/provider_values_yaml.j2 +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/generator/values_yaml.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/metadata.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/helm/url_safety_registration.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/logging_config.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/middleware/context.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/monitoring/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/monitoring/metrics.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/monitoring/setup.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/common.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/crawl.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/search.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/presets/types.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/app.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/base.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/client.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/monitoring.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/base.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/bing_agent.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/brave.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/firecrawl.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/google.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/jina.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/perplexity.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/tavily.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/providers/vertexai_agent.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/settings/secret_str.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/startup_report.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/unique_search_proxy_client/web/utils/__init__.py +0 -0
- {unique_search_proxy-2026.44.0.dev9 → unique_search_proxy-2026.44.0.dev11}/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.44.0.
|
|
3
|
+
Version: 2026.44.0.dev11
|
|
4
4
|
Summary: Web Search Proxy implementation
|
|
5
5
|
Author: ThePhilAz
|
|
6
6
|
Author-email: ThePhilAz <rami.azouz@philico.com>
|
|
@@ -21,7 +21,7 @@ Requires-Dist: google-genai>=1.73.0,<2
|
|
|
21
21
|
Requires-Dist: google-auth>=2.43.0,<3
|
|
22
22
|
Requires-Dist: jinja2>=3.1.6,<4
|
|
23
23
|
Requires-Dist: unique-toolkit[monitoring]>=2026.44.0.dev9,<2026.44.0rc0
|
|
24
|
-
Requires-Dist: unique-search-proxy-core>=2026.44.0.
|
|
24
|
+
Requires-Dist: unique-search-proxy-core>=2026.44.0.dev9,<2026.44.0rc0
|
|
25
25
|
Requires-Dist: openai>=1.105.0,<3
|
|
26
26
|
Requires-Python: >=3.12
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique-search-proxy"
|
|
3
|
-
version = "2026.44.0.
|
|
3
|
+
version = "2026.44.0.dev11"
|
|
4
4
|
description = "Web Search Proxy implementation"
|
|
5
5
|
authors = [{ name = "ThePhilAz", email = "rami.azouz@philico.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -23,7 +23,7 @@ dependencies = [
|
|
|
23
23
|
"google-auth>=2.43.0,<3",
|
|
24
24
|
"jinja2>=3.1.6,<4",
|
|
25
25
|
"unique-toolkit[monitoring]>=2026.44.0.dev9,<2026.44.0rc0",
|
|
26
|
-
"unique-search-proxy-core>=2026.44.0.
|
|
26
|
+
"unique-search-proxy-core>=2026.44.0.dev9,<2026.44.0rc0",
|
|
27
27
|
"openai>=1.105.0,<3",
|
|
28
28
|
]
|
|
29
29
|
|
|
@@ -9,6 +9,7 @@ from unique_search_proxy_core.crawlers.config_types import CrawlRequest
|
|
|
9
9
|
from unique_search_proxy_core.errors import (
|
|
10
10
|
ProxyError,
|
|
11
11
|
UpstreamTimeoutError,
|
|
12
|
+
ValidationProxyError,
|
|
12
13
|
attach_request_context,
|
|
13
14
|
)
|
|
14
15
|
from unique_search_proxy_core.schema import (
|
|
@@ -35,6 +36,9 @@ from unique_search_proxy_client.web.monitoring.metrics import (
|
|
|
35
36
|
record_crawl_success,
|
|
36
37
|
record_crawl_url_outcomes,
|
|
37
38
|
)
|
|
39
|
+
from unique_search_proxy_client.web.settings.request_limits import (
|
|
40
|
+
request_limits_settings,
|
|
41
|
+
)
|
|
38
42
|
|
|
39
43
|
router = APIRouter(tags=["crawl"])
|
|
40
44
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -48,6 +52,21 @@ def _crawl_request_context(exc: ProxyError, *, crawler_id: str) -> ProxyError:
|
|
|
48
52
|
)
|
|
49
53
|
|
|
50
54
|
|
|
55
|
+
def _enforce_url_limits(urls: list[str]) -> None:
|
|
56
|
+
limits = request_limits_settings
|
|
57
|
+
if len(urls) > limits.max_urls_per_crawl:
|
|
58
|
+
raise ValidationProxyError(
|
|
59
|
+
f"Crawl request lists {len(urls)} URLs; "
|
|
60
|
+
f"at most {limits.max_urls_per_crawl} are allowed",
|
|
61
|
+
)
|
|
62
|
+
for index, url in enumerate(urls):
|
|
63
|
+
if len(url) > limits.max_url_length:
|
|
64
|
+
raise ValidationProxyError(
|
|
65
|
+
f"URL at index {index} is {len(url)} characters long; "
|
|
66
|
+
f"at most {limits.max_url_length} are allowed",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
51
70
|
def _url_outcomes(results: list[CrawlUrlResult]) -> list[tuple[str, str, str]]:
|
|
52
71
|
"""Derive ``(outcome, error_code, http_status)`` triples from merged results."""
|
|
53
72
|
outcomes: list[tuple[str, str, str]] = []
|
|
@@ -84,6 +103,7 @@ async def crawl(
|
|
|
84
103
|
)
|
|
85
104
|
|
|
86
105
|
try:
|
|
106
|
+
_enforce_url_limits(body.urls)
|
|
87
107
|
async with asyncio.timeout(timeout):
|
|
88
108
|
registry = get_http_client_registry(request.app)
|
|
89
109
|
client = await registry.client_for(get_request_context())
|
|
@@ -11,7 +11,7 @@ from unique_search_proxy_core.crawlers.basic.processing.policy import (
|
|
|
11
11
|
)
|
|
12
12
|
from unique_search_proxy_core.crawlers.basic.schema import BasicCrawlRequest
|
|
13
13
|
from unique_search_proxy_core.errors import ResponseTooLargeError
|
|
14
|
-
from unique_search_proxy_core.http_client import read_capped_body
|
|
14
|
+
from unique_search_proxy_core.http_client import ByteBudget, read_capped_body
|
|
15
15
|
from unique_search_proxy_core.schema import (
|
|
16
16
|
CrawlUrlResult,
|
|
17
17
|
ProxyErrorCode,
|
|
@@ -40,6 +40,9 @@ from unique_search_proxy_client.web.core.provider_response import (
|
|
|
40
40
|
from unique_search_proxy_client.web.core.url_safety.gate import AllowedCrawlTarget
|
|
41
41
|
from unique_search_proxy_client.web.monitoring.metrics import record_crawl_blocked
|
|
42
42
|
from unique_search_proxy_client.web.settings.client import http_client_settings
|
|
43
|
+
from unique_search_proxy_client.web.settings.request_limits import (
|
|
44
|
+
request_limits_settings,
|
|
45
|
+
)
|
|
43
46
|
|
|
44
47
|
_LOGGER = logging.getLogger(__name__)
|
|
45
48
|
|
|
@@ -106,6 +109,7 @@ class BasicCrawlerService(BaseCrawler[BasicCrawlRequest]):
|
|
|
106
109
|
|
|
107
110
|
timeout = request.timeout
|
|
108
111
|
semaphore = asyncio.Semaphore(request.max_concurrent_requests)
|
|
112
|
+
budget = ByteBudget(request_limits_settings.max_crawl_response_bytes)
|
|
109
113
|
return list(
|
|
110
114
|
await asyncio.gather(
|
|
111
115
|
*[
|
|
@@ -115,6 +119,7 @@ class BasicCrawlerService(BaseCrawler[BasicCrawlRequest]):
|
|
|
115
119
|
resolved_target=allowed_target.resolved,
|
|
116
120
|
timeout=timeout,
|
|
117
121
|
semaphore=semaphore,
|
|
122
|
+
budget=budget,
|
|
118
123
|
content_type_handlers=request.content_types.to_handlers(),
|
|
119
124
|
)
|
|
120
125
|
for allowed_target in allowed_targets
|
|
@@ -130,9 +135,17 @@ class BasicCrawlerService(BaseCrawler[BasicCrawlRequest]):
|
|
|
130
135
|
resolved_target: ResolvedCrawlTarget,
|
|
131
136
|
timeout: int,
|
|
132
137
|
semaphore: asyncio.Semaphore,
|
|
138
|
+
budget: ByteBudget,
|
|
133
139
|
content_type_handlers: dict[str, ContentTypeHandlerPolicy],
|
|
134
140
|
) -> CrawlUrlResult:
|
|
135
141
|
async with semaphore:
|
|
142
|
+
if budget.exhausted:
|
|
143
|
+
return crawl_upstream_error(
|
|
144
|
+
display_url,
|
|
145
|
+
"Crawl response budget exhausted before this URL was fetched",
|
|
146
|
+
content_type=None,
|
|
147
|
+
code=ProxyErrorCode.RESPONSE_TOO_LARGE.value,
|
|
148
|
+
)
|
|
136
149
|
try:
|
|
137
150
|
async with safe_pinned_httpx_stream(
|
|
138
151
|
client,
|
|
@@ -161,6 +174,7 @@ class BasicCrawlerService(BaseCrawler[BasicCrawlRequest]):
|
|
|
161
174
|
body = await read_capped_body(
|
|
162
175
|
response,
|
|
163
176
|
max_bytes=http_client_settings.max_response_bytes,
|
|
177
|
+
budget=budget,
|
|
164
178
|
)
|
|
165
179
|
raw_body = body.decode(
|
|
166
180
|
response.encoding or "utf-8",
|
|
@@ -31,6 +31,9 @@ from unique_search_proxy_client.web.settings.providers import (
|
|
|
31
31
|
tavily_crawl_credentials,
|
|
32
32
|
vertexai_agent_credentials,
|
|
33
33
|
)
|
|
34
|
+
from unique_search_proxy_client.web.settings.request_limits import (
|
|
35
|
+
request_limits_settings,
|
|
36
|
+
)
|
|
34
37
|
|
|
35
38
|
|
|
36
39
|
@dataclass(frozen=True)
|
|
@@ -61,6 +64,7 @@ _REGISTERED_INSTANCES: tuple[BaseSettings, ...] = (
|
|
|
61
64
|
jina_crawl_credentials,
|
|
62
65
|
firecrawl_crawl_credentials,
|
|
63
66
|
http_client_settings,
|
|
67
|
+
request_limits_settings,
|
|
64
68
|
prometheus_settings,
|
|
65
69
|
)
|
|
66
70
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Server-side ceilings that bound what a single proxy request may consume."""
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
|
+
|
|
6
|
+
from unique_search_proxy_client.web.helm.metadata import helm_settings
|
|
7
|
+
from unique_search_proxy_client.web.settings.base import get_settings
|
|
8
|
+
|
|
9
|
+
REQUEST_LIMITS_ENV_PREFIX = "REQUEST_LIMITS_"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@helm_settings(
|
|
13
|
+
title="Request Limits",
|
|
14
|
+
helm_key="requestLimits",
|
|
15
|
+
kind="internal",
|
|
16
|
+
gated=False,
|
|
17
|
+
egress=None,
|
|
18
|
+
env_prefix=REQUEST_LIMITS_ENV_PREFIX,
|
|
19
|
+
sections={
|
|
20
|
+
"crawl": ["max_crawl_response_kib", "max_urls_per_crawl", "max_url_length"],
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
class RequestLimitsSettings(BaseSettings):
|
|
24
|
+
"""Ceilings callers cannot raise, sized against the pod memory limit.
|
|
25
|
+
|
|
26
|
+
Byte sizes are in KiB: Helm renders numbers >= 1e6 in scientific notation,
|
|
27
|
+
which pydantic cannot parse back into an int.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
model_config = SettingsConfigDict(
|
|
31
|
+
env_prefix=REQUEST_LIMITS_ENV_PREFIX,
|
|
32
|
+
extra="ignore",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
max_crawl_response_kib: int = Field(
|
|
36
|
+
default=10 * 1024,
|
|
37
|
+
ge=1,
|
|
38
|
+
description=(
|
|
39
|
+
"Combined decoded size, in KiB, of all page bodies one crawl request "
|
|
40
|
+
"may keep. URLs read after the budget is spent fail individually."
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Matches the search fetch_size ceiling: the web search tool crawls a whole
|
|
45
|
+
# result page in one request.
|
|
46
|
+
max_urls_per_crawl: int = Field(
|
|
47
|
+
default=100,
|
|
48
|
+
ge=1,
|
|
49
|
+
description=(
|
|
50
|
+
"Maximum number of URLs in one crawl request. Requests with more "
|
|
51
|
+
"are rejected with a 422."
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
max_url_length: int = Field(
|
|
55
|
+
default=2048,
|
|
56
|
+
ge=1,
|
|
57
|
+
description=(
|
|
58
|
+
"Maximum length of each URL, in characters. Requests containing a "
|
|
59
|
+
"longer URL are rejected with a 422."
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def max_crawl_response_bytes(self) -> int:
|
|
65
|
+
return self.max_crawl_response_kib * 1024
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_request_limits_settings() -> RequestLimitsSettings:
|
|
69
|
+
return get_settings(RequestLimitsSettings, env_prefix=REQUEST_LIMITS_ENV_PREFIX)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
request_limits_settings: RequestLimitsSettings = get_request_limits_settings()
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
"REQUEST_LIMITS_ENV_PREFIX",
|
|
76
|
+
"RequestLimitsSettings",
|
|
77
|
+
"get_request_limits_settings",
|
|
78
|
+
"request_limits_settings",
|
|
79
|
+
]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|