unique-search-proxy-core 2026.32.0.dev1__tar.gz → 2026.32.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.
Files changed (54) hide show
  1. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/PKG-INFO +1 -1
  2. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/pyproject.toml +1 -1
  3. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/service.py +31 -0
  4. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/README.md +0 -0
  5. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/__init__.py +0 -0
  6. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/__init__.py +0 -0
  7. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/base.py +0 -0
  8. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/bing/__init__.py +0 -0
  9. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/bing/schema.py +0 -0
  10. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/config_types.py +0 -0
  11. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/output_schema.py +0 -0
  12. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/resolve.py +0 -0
  13. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/response_parsing.py +0 -0
  14. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/vertexai/__init__.py +0 -0
  15. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/agent_engines/vertexai/schema.py +0 -0
  16. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/context.py +0 -0
  17. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/__init__.py +0 -0
  18. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/base.py +0 -0
  19. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/basic/__init__.py +0 -0
  20. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/basic/content_types.py +0 -0
  21. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/basic/processing/policy.py +0 -0
  22. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/basic/schema.py +0 -0
  23. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/config_types.py +0 -0
  24. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/firecrawl/schema.py +0 -0
  25. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/jina/schema.py +0 -0
  26. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/crawlers/tavily/schema.py +0 -0
  27. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/errors.py +0 -0
  28. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/logging.py +0 -0
  29. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/param_policy/__init__.py +0 -0
  30. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/param_policy/annotations.py +0 -0
  31. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/param_policy/derive.py +0 -0
  32. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/param_policy/exposable_param.py +0 -0
  33. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/param_policy/exposed_params.py +0 -0
  34. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/param_policy/request_base.py +0 -0
  35. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/providers/schema.py +0 -0
  36. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/schema.py +0 -0
  37. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/__init__.py +0 -0
  38. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/base.py +0 -0
  39. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/brave/__init__.py +0 -0
  40. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/brave/schema.py +0 -0
  41. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/config_types.py +0 -0
  42. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/google/__init__.py +0 -0
  43. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/google/schema.py +0 -0
  44. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/perplexity/__init__.py +0 -0
  45. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/search_engines/perplexity/schema.py +0 -0
  46. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/__init__.py +0 -0
  47. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/dns.py +0 -0
  48. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/egress.py +0 -0
  49. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/models.py +0 -0
  50. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/netloc.py +0 -0
  51. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/policy.py +0 -0
  52. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/redirect.py +0 -0
  53. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/resolver.py +0 -0
  54. {unique_search_proxy_core-2026.32.0.dev1 → unique_search_proxy_core-2026.32.0.dev2}/unique_search_proxy_core/url_safety/settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-search-proxy-core
3
- Version: 2026.32.0.dev1
3
+ Version: 2026.32.0.dev2
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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique-search-proxy-core"
3
- version = "2026.32.0.dev1"
3
+ version = "2026.32.0.dev2"
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"
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  from ipaddress import ip_address
4
5
  from urllib.parse import urlsplit
5
6
 
6
7
  from unique_search_proxy_core.url_safety import dns, redirect, resolver
7
8
  from unique_search_proxy_core.url_safety.models import (
9
+ BlockedCrawlTarget,
8
10
  CrawlTargetValidationError,
9
11
  ResolvedCrawlTarget,
10
12
  UrlSafetyOutcome,
@@ -13,6 +15,21 @@ from unique_search_proxy_core.url_safety.models import (
13
15
  from unique_search_proxy_core.url_safety.policy import validate_target_cheap
14
16
  from unique_search_proxy_core.url_safety.settings import url_safety_settings
15
17
 
18
+ _LOGGER = logging.getLogger(__name__)
19
+
20
+
21
+ def _safe_hostname(url: str) -> str | None:
22
+ """Extract the hostname from a URL without ever raising.
23
+
24
+ ``urlsplit(...).hostname`` raises ``ValueError`` for malformed IPv6 hosts
25
+ (e.g. ``http://[::1``). This must never happen on the fail-closed path,
26
+ where a single bad URL would otherwise abort the whole batch.
27
+ """
28
+ try:
29
+ return urlsplit(url).hostname
30
+ except ValueError:
31
+ return None
32
+
16
33
 
17
34
  class UrlSafetyService:
18
35
  @staticmethod
@@ -37,6 +54,20 @@ class UrlSafetyService:
37
54
  except CrawlTargetValidationError as exc:
38
55
  blocked = exc.blocked_targets[0]
39
56
  outcomes.append(UrlSafetyOutcome(url=url, blocked=blocked))
57
+ except Exception:
58
+ # Fail closed: an unexpected error while validating a single URL
59
+ # must block only that URL, never fail the whole batch.
60
+ _LOGGER.exception("URL safety validation failed unexpectedly")
61
+ outcomes.append(
62
+ UrlSafetyOutcome(
63
+ url=url,
64
+ blocked=BlockedCrawlTarget(
65
+ hostname=_safe_hostname(working_url),
66
+ category="validation_error",
67
+ reason="URL safety validation failed unexpectedly",
68
+ ),
69
+ )
70
+ )
40
71
  return outcomes
41
72
 
42
73
  @staticmethod