gavio 0.1.0__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 (62) hide show
  1. gavio-0.1.0/.gitignore +65 -0
  2. gavio-0.1.0/PKG-INFO +140 -0
  3. gavio-0.1.0/README.md +93 -0
  4. gavio-0.1.0/gavio/__init__.py +63 -0
  5. gavio-0.1.0/gavio/_ids.py +64 -0
  6. gavio-0.1.0/gavio/context.py +44 -0
  7. gavio-0.1.0/gavio/exceptions.py +53 -0
  8. gavio-0.1.0/gavio/gateway.py +203 -0
  9. gavio-0.1.0/gavio/interceptors/__init__.py +8 -0
  10. gavio-0.1.0/gavio/interceptors/audit/__init__.py +16 -0
  11. gavio-0.1.0/gavio/interceptors/audit/interceptor.py +87 -0
  12. gavio-0.1.0/gavio/interceptors/audit/record.py +69 -0
  13. gavio-0.1.0/gavio/interceptors/audit/sink.py +19 -0
  14. gavio-0.1.0/gavio/interceptors/audit/sinks/__init__.py +7 -0
  15. gavio-0.1.0/gavio/interceptors/audit/sinks/stdout.py +41 -0
  16. gavio-0.1.0/gavio/interceptors/base.py +45 -0
  17. gavio-0.1.0/gavio/interceptors/cache/__init__.py +8 -0
  18. gavio-0.1.0/gavio/interceptors/cache/backend.py +31 -0
  19. gavio-0.1.0/gavio/interceptors/cache/backends/__init__.py +7 -0
  20. gavio-0.1.0/gavio/interceptors/cache/backends/memory.py +44 -0
  21. gavio-0.1.0/gavio/interceptors/chain.py +63 -0
  22. gavio-0.1.0/gavio/interceptors/pii/__init__.py +39 -0
  23. gavio-0.1.0/gavio/interceptors/pii/context.py +24 -0
  24. gavio-0.1.0/gavio/interceptors/pii/guard.py +170 -0
  25. gavio-0.1.0/gavio/interceptors/pii/match.py +32 -0
  26. gavio-0.1.0/gavio/interceptors/pii/scanner.py +63 -0
  27. gavio-0.1.0/gavio/interceptors/pii/scanners/__init__.py +37 -0
  28. gavio-0.1.0/gavio/interceptors/pii/scanners/bsn.py +42 -0
  29. gavio-0.1.0/gavio/interceptors/pii/scanners/credit_card.py +48 -0
  30. gavio-0.1.0/gavio/interceptors/pii/scanners/email.py +32 -0
  31. gavio-0.1.0/gavio/interceptors/pii/scanners/iban.py +47 -0
  32. gavio-0.1.0/gavio/interceptors/pii/scanners/ip_address.py +45 -0
  33. gavio-0.1.0/gavio/interceptors/pii/scanners/phone.py +45 -0
  34. gavio-0.1.0/gavio/interceptors/pii/scanners/secret.py +51 -0
  35. gavio-0.1.0/gavio/interceptors/pii/scanners/ssn.py +32 -0
  36. gavio-0.1.0/gavio/interceptors/reliability/__init__.py +15 -0
  37. gavio-0.1.0/gavio/interceptors/reliability/fallback.py +63 -0
  38. gavio-0.1.0/gavio/interceptors/reliability/policy.py +30 -0
  39. gavio-0.1.0/gavio/interceptors/reliability/retry.py +91 -0
  40. gavio-0.1.0/gavio/interceptors/reliability/timeout.py +41 -0
  41. gavio-0.1.0/gavio/pricing.py +73 -0
  42. gavio-0.1.0/gavio/providers/__init__.py +43 -0
  43. gavio-0.1.0/gavio/providers/_http.py +51 -0
  44. gavio-0.1.0/gavio/providers/anthropic.py +99 -0
  45. gavio-0.1.0/gavio/providers/base.py +60 -0
  46. gavio-0.1.0/gavio/providers/mock.py +69 -0
  47. gavio-0.1.0/gavio/providers/openai.py +82 -0
  48. gavio-0.1.0/gavio/py.typed +0 -0
  49. gavio-0.1.0/gavio/request.py +57 -0
  50. gavio-0.1.0/gavio/response.py +49 -0
  51. gavio-0.1.0/gavio/testing/__init__.py +9 -0
  52. gavio-0.1.0/gavio/testing/fixtures.py +28 -0
  53. gavio-0.1.0/gavio/testing/harness.py +99 -0
  54. gavio-0.1.0/gavio/types.py +75 -0
  55. gavio-0.1.0/pyproject.toml +61 -0
  56. gavio-0.1.0/tests/unit/test_audit_and_testkit.py +66 -0
  57. gavio-0.1.0/tests/unit/test_chain_and_retry.py +118 -0
  58. gavio-0.1.0/tests/unit/test_gateway.py +99 -0
  59. gavio-0.1.0/tests/unit/test_ids.py +22 -0
  60. gavio-0.1.0/tests/unit/test_pii_guard.py +80 -0
  61. gavio-0.1.0/tests/unit/test_pii_scanners.py +79 -0
  62. gavio-0.1.0/tests/unit/test_vectors.py +72 -0
gavio-0.1.0/.gitignore ADDED
@@ -0,0 +1,65 @@
1
+ # ─── OS / editor ──────────────────────────────────────────────
2
+ .DS_Store
3
+ Thumbs.db
4
+ *.swp
5
+ *~
6
+ .idea/
7
+ .vscode/
8
+ *.iml
9
+
10
+ # ─── Node / TypeScript (gavio-js) ─────────────────────────────
11
+ node_modules/
12
+ dist/
13
+ build/
14
+ out/
15
+ coverage/
16
+ *.tsbuildinfo
17
+ .npm/
18
+ .pnpm-store/
19
+ .yarn/
20
+ npm-debug.log*
21
+ yarn-error.log*
22
+ .vitest/
23
+
24
+ # ─── Python (gavio-py) ────────────────────────────────────────
25
+ .venv/
26
+ venv/
27
+ env/
28
+ __pycache__/
29
+ *.py[cod]
30
+ *.egg-info/
31
+ .eggs/
32
+ .pytest_cache/
33
+ .mypy_cache/
34
+ .ruff_cache/
35
+ .tox/
36
+ .coverage
37
+ .coverage.*
38
+ htmlcov/
39
+
40
+ # ─── Java / Maven / Gradle (gavio-java) ───────────────────────
41
+ target/
42
+ .gradle/
43
+ *.class
44
+ *.jar
45
+ *.war
46
+ hs_err_pid*
47
+
48
+ # ─── Secrets / local env ──────────────────────────────────────
49
+ .env
50
+ .env.*
51
+ !.env.example
52
+ *.pem
53
+ *.key
54
+
55
+ # ─── SigMap local artifacts ───────────────────────────────────
56
+ # Generated context (copilot-instructions.md) is committed on purpose;
57
+ # only the local metrics/cache are ignored.
58
+ .context/
59
+ .sigmap-cache/
60
+
61
+
62
+ # ---docs--- #
63
+ MASTER_*.md
64
+ SDK_*.md
65
+ *PLAN.md
gavio-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: gavio
3
+ Version: 0.1.0
4
+ Summary: The open standard AI gateway for production systems — PII protection, audit trails, reliability, and cost control as composable interceptors.
5
+ Project-URL: Homepage, https://gavio.io
6
+ Project-URL: Repository, https://github.com/gavio-ai/gavio
7
+ Project-URL: Changelog, https://github.com/gavio-ai/gavio/blob/main/CHANGELOG.md
8
+ Author: Gavio core team
9
+ License: MIT
10
+ Keywords: ai,anthropic,audit,gateway,interceptor,llm,openai,pii
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.10
19
+ Provides-Extra: all
20
+ Requires-Dist: elasticsearch; extra == 'all'
21
+ Requires-Dist: opentelemetry-exporter-otlp; extra == 'all'
22
+ Requires-Dist: opentelemetry-sdk; extra == 'all'
23
+ Requires-Dist: pgvector; extra == 'all'
24
+ Requires-Dist: presidio-analyzer; extra == 'all'
25
+ Requires-Dist: psycopg2; extra == 'all'
26
+ Requires-Dist: redis; extra == 'all'
27
+ Requires-Dist: spacy; extra == 'all'
28
+ Provides-Extra: dev
29
+ Requires-Dist: mypy>=1.8; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
31
+ Requires-Dist: pytest>=7.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.4; extra == 'dev'
33
+ Provides-Extra: elasticsearch
34
+ Requires-Dist: elasticsearch; extra == 'elasticsearch'
35
+ Provides-Extra: otel
36
+ Requires-Dist: opentelemetry-exporter-otlp; extra == 'otel'
37
+ Requires-Dist: opentelemetry-sdk; extra == 'otel'
38
+ Provides-Extra: pgvector
39
+ Requires-Dist: pgvector; extra == 'pgvector'
40
+ Requires-Dist: psycopg2; extra == 'pgvector'
41
+ Provides-Extra: presidio
42
+ Requires-Dist: presidio-analyzer; extra == 'presidio'
43
+ Requires-Dist: spacy; extra == 'presidio'
44
+ Provides-Extra: redis
45
+ Requires-Dist: redis; extra == 'redis'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # Gavio — Python SDK
49
+
50
+ > The open standard AI gateway for production systems. PII protection, audit
51
+ > trails, reliability, and cost control as composable interceptors.
52
+
53
+ `gavio` sits between your application and any LLM provider. The same request
54
+ passes through a pre/post interceptor chain — PII redaction, retries, cost
55
+ tracking, audit logging — before and after the provider call.
56
+
57
+ Part of the [Gavio](https://gavio.io) project. MIT licensed.
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install gavio # zero mandatory dependencies
63
+ pip install gavio[dev] # + pytest, ruff, mypy
64
+ ```
65
+
66
+ Requires Python 3.10+.
67
+
68
+ ## Quick start (dev mode — no API key, no network)
69
+
70
+ ```python
71
+ import asyncio
72
+ from gavio import Gateway
73
+ from gavio.interceptors.pii import PiiGuard
74
+
75
+ gw = (
76
+ Gateway.builder()
77
+ .dev_mode(True) # MockProvider + stdout audit
78
+ .use(PiiGuard()) # redact PII before it leaves the process
79
+ .build()
80
+ )
81
+
82
+ async def main():
83
+ resp = await gw.complete(
84
+ messages=[{"role": "user", "content": "Email jan@example.com about NL91ABNA0417164300"}],
85
+ agent_id="demo",
86
+ )
87
+ print(resp.content) # PII restored in the reply
88
+ print(f"cost=${resp.cost_usd:.6f} latency={resp.latency_ms}ms")
89
+ print("pii types:", resp.audit.pii_entity_types)
90
+
91
+ asyncio.run(main())
92
+ ```
93
+
94
+ ## Real providers
95
+
96
+ ```python
97
+ from gavio import Gateway, Provider
98
+ from gavio.interceptors.pii import PiiGuard
99
+ from gavio.interceptors.audit import AuditInterceptor
100
+ from gavio.interceptors.reliability import RetryInterceptor, TimeoutPolicy
101
+
102
+ gw = (
103
+ Gateway.builder()
104
+ .provider(Provider.ANTHROPIC) # reads ANTHROPIC_API_KEY
105
+ .model("claude-sonnet-4-6")
106
+ .use(PiiGuard(sensitivity="strict"))
107
+ .use(AuditInterceptor(sink="stdout://"))
108
+ .use(TimeoutPolicy(timeout_seconds=30))
109
+ .use(RetryInterceptor(max_attempts=3))
110
+ .build()
111
+ )
112
+
113
+ resp = await gw.complete(messages=[{"role": "user", "content": "Hi"}])
114
+ ```
115
+
116
+ `OPENAI_API_KEY` / `Provider.OPENAI` work the same way.
117
+
118
+ ## What ships in v0.1.0
119
+
120
+ - **Core** — `Gateway` fluent builder, `InterceptorChain`, `GavioRequest` /
121
+ `GavioResponse`, UUID v7 `trace_id`, `agent_id` / `parent_trace_id`.
122
+ - **PII Guard (F-SEC-01)** — Email, IBAN (mod-97), BSN (11-proef),
123
+ CreditCard (Luhn), Phone, IP, SSN scanners, redact/mask/tag/block, restore.
124
+ - **Secret Scanner (F-SEC-04)** — API keys, JWTs, PEM keys, DB URLs.
125
+ - **Reliability** — retry with backoff (F-REL-01), fallback chain (F-REL-02),
126
+ timeout (F-REL-07).
127
+ - **Cost tracking (F-GOV-01)** — per-request `cost_usd`.
128
+ - **Audit (F-OBS-01)** — `AuditRecord` + `StdoutSink` (F-OBS-05).
129
+ - **Dev mode (F-DX-01)** and **dry-run mode (F-DX-02)**.
130
+ - **Providers** — OpenAI, Anthropic, Mock.
131
+
132
+ See the [Python guide](../../docs/packages/python.md) and [CHANGELOG.md](../../CHANGELOG.md).
133
+
134
+ ## Tests
135
+
136
+ ```bash
137
+ pip install -e ".[dev]"
138
+ pytest tests/unit -v
139
+ ruff check gavio
140
+ ```
gavio-0.1.0/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # Gavio — Python SDK
2
+
3
+ > The open standard AI gateway for production systems. PII protection, audit
4
+ > trails, reliability, and cost control as composable interceptors.
5
+
6
+ `gavio` sits between your application and any LLM provider. The same request
7
+ passes through a pre/post interceptor chain — PII redaction, retries, cost
8
+ tracking, audit logging — before and after the provider call.
9
+
10
+ Part of the [Gavio](https://gavio.io) project. MIT licensed.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install gavio # zero mandatory dependencies
16
+ pip install gavio[dev] # + pytest, ruff, mypy
17
+ ```
18
+
19
+ Requires Python 3.10+.
20
+
21
+ ## Quick start (dev mode — no API key, no network)
22
+
23
+ ```python
24
+ import asyncio
25
+ from gavio import Gateway
26
+ from gavio.interceptors.pii import PiiGuard
27
+
28
+ gw = (
29
+ Gateway.builder()
30
+ .dev_mode(True) # MockProvider + stdout audit
31
+ .use(PiiGuard()) # redact PII before it leaves the process
32
+ .build()
33
+ )
34
+
35
+ async def main():
36
+ resp = await gw.complete(
37
+ messages=[{"role": "user", "content": "Email jan@example.com about NL91ABNA0417164300"}],
38
+ agent_id="demo",
39
+ )
40
+ print(resp.content) # PII restored in the reply
41
+ print(f"cost=${resp.cost_usd:.6f} latency={resp.latency_ms}ms")
42
+ print("pii types:", resp.audit.pii_entity_types)
43
+
44
+ asyncio.run(main())
45
+ ```
46
+
47
+ ## Real providers
48
+
49
+ ```python
50
+ from gavio import Gateway, Provider
51
+ from gavio.interceptors.pii import PiiGuard
52
+ from gavio.interceptors.audit import AuditInterceptor
53
+ from gavio.interceptors.reliability import RetryInterceptor, TimeoutPolicy
54
+
55
+ gw = (
56
+ Gateway.builder()
57
+ .provider(Provider.ANTHROPIC) # reads ANTHROPIC_API_KEY
58
+ .model("claude-sonnet-4-6")
59
+ .use(PiiGuard(sensitivity="strict"))
60
+ .use(AuditInterceptor(sink="stdout://"))
61
+ .use(TimeoutPolicy(timeout_seconds=30))
62
+ .use(RetryInterceptor(max_attempts=3))
63
+ .build()
64
+ )
65
+
66
+ resp = await gw.complete(messages=[{"role": "user", "content": "Hi"}])
67
+ ```
68
+
69
+ `OPENAI_API_KEY` / `Provider.OPENAI` work the same way.
70
+
71
+ ## What ships in v0.1.0
72
+
73
+ - **Core** — `Gateway` fluent builder, `InterceptorChain`, `GavioRequest` /
74
+ `GavioResponse`, UUID v7 `trace_id`, `agent_id` / `parent_trace_id`.
75
+ - **PII Guard (F-SEC-01)** — Email, IBAN (mod-97), BSN (11-proef),
76
+ CreditCard (Luhn), Phone, IP, SSN scanners, redact/mask/tag/block, restore.
77
+ - **Secret Scanner (F-SEC-04)** — API keys, JWTs, PEM keys, DB URLs.
78
+ - **Reliability** — retry with backoff (F-REL-01), fallback chain (F-REL-02),
79
+ timeout (F-REL-07).
80
+ - **Cost tracking (F-GOV-01)** — per-request `cost_usd`.
81
+ - **Audit (F-OBS-01)** — `AuditRecord` + `StdoutSink` (F-OBS-05).
82
+ - **Dev mode (F-DX-01)** and **dry-run mode (F-DX-02)**.
83
+ - **Providers** — OpenAI, Anthropic, Mock.
84
+
85
+ See the [Python guide](../../docs/packages/python.md) and [CHANGELOG.md](../../CHANGELOG.md).
86
+
87
+ ## Tests
88
+
89
+ ```bash
90
+ pip install -e ".[dev]"
91
+ pytest tests/unit -v
92
+ ruff check gavio
93
+ ```
@@ -0,0 +1,63 @@
1
+ """Gavio — the open standard AI gateway for production systems.
2
+
3
+ Public API surface (v0.1.0):
4
+
5
+ from gavio import Gateway, GavioRequest, GavioResponse, Provider
6
+
7
+ See https://gavio.io for documentation. MIT licensed.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from .context import InterceptorContext
13
+ from .exceptions import (
14
+ BudgetExceededError,
15
+ ConfigurationError,
16
+ GavioError,
17
+ GuardrailViolationError,
18
+ PiiBlockedError,
19
+ ProviderError,
20
+ ProviderUnavailableError,
21
+ RateLimitError,
22
+ ServerError,
23
+ )
24
+ from .gateway import Gateway, GatewayBuilder
25
+ from .request import GavioRequest
26
+ from .response import GavioResponse
27
+ from .types import (
28
+ CacheType,
29
+ GuardrailOutcome,
30
+ Message,
31
+ PiiMode,
32
+ Provider,
33
+ Sensitivity,
34
+ TokenUsage,
35
+ )
36
+
37
+ __version__ = "0.1.0"
38
+
39
+ __all__ = [
40
+ "__version__",
41
+ "Gateway",
42
+ "GatewayBuilder",
43
+ "GavioRequest",
44
+ "GavioResponse",
45
+ "InterceptorContext",
46
+ "Provider",
47
+ "Message",
48
+ "TokenUsage",
49
+ "CacheType",
50
+ "PiiMode",
51
+ "Sensitivity",
52
+ "GuardrailOutcome",
53
+ # exceptions
54
+ "GavioError",
55
+ "ConfigurationError",
56
+ "ProviderError",
57
+ "ProviderUnavailableError",
58
+ "RateLimitError",
59
+ "ServerError",
60
+ "PiiBlockedError",
61
+ "BudgetExceededError",
62
+ "GuardrailViolationError",
63
+ ]
@@ -0,0 +1,64 @@
1
+ """UUID v7 generation — time-sortable, unique identifiers for traces.
2
+
3
+ Python's stdlib only gains ``uuid.uuid7`` in 3.14, so we ship a compliant
4
+ generator here. UUID v7 layout (RFC 9562): 48-bit Unix millisecond timestamp,
5
+ 4-bit version, 12 bits of randomness, 2-bit variant, 62 bits of randomness.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import threading
12
+ import time
13
+ import uuid
14
+
15
+ _lock = threading.Lock()
16
+ _last_ms = -1
17
+ _seq = 0 # 12-bit per-millisecond sequence in rand_a, for monotonicity
18
+
19
+
20
+ def _next_timestamp_and_seq() -> tuple[int, int]:
21
+ """Return a (unix_ms, sequence) pair that is monotonically non-decreasing.
22
+
23
+ Within a single millisecond the 12-bit sequence increments so IDs stay
24
+ strictly ordered (RFC 9562 method 1). If the sequence overflows, the
25
+ timestamp is nudged forward.
26
+ """
27
+ global _last_ms, _seq
28
+ with _lock:
29
+ now_ms = int(time.time() * 1000)
30
+ if now_ms > _last_ms:
31
+ _last_ms = now_ms
32
+ _seq = int.from_bytes(os.urandom(2), "big") & 0x0FFF
33
+ else:
34
+ _seq += 1
35
+ if _seq > 0x0FFF:
36
+ _last_ms += 1
37
+ _seq = 0
38
+ return _last_ms, _seq
39
+
40
+
41
+ def uuid7() -> uuid.UUID:
42
+ """Return a new UUID version 7 (time-ordered, monotonic within a process)."""
43
+ unix_ms, rand_a = _next_timestamp_and_seq()
44
+
45
+ # 48 bits of millisecond timestamp.
46
+ time_high = (unix_ms >> 16) & 0xFFFFFFFF
47
+ time_low = unix_ms & 0xFFFF
48
+
49
+ rand_b = int.from_bytes(os.urandom(8), "big") & 0x3FFFFFFFFFFFFFFF # 62 bits
50
+
51
+ value = (
52
+ (time_high << 96)
53
+ | (time_low << 80)
54
+ | (0x7 << 76) # version 7
55
+ | (rand_a << 64)
56
+ | (0b10 << 62) # variant
57
+ | rand_b
58
+ )
59
+ return uuid.UUID(int=value)
60
+
61
+
62
+ def new_trace_id() -> str:
63
+ """Return a fresh trace id as a string."""
64
+ return str(uuid7())
@@ -0,0 +1,44 @@
1
+ """Per-request context passed through the interceptor pipeline."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ @dataclass
10
+ class InterceptorContext:
11
+ """Mutable scratch space shared by all interceptors within one request.
12
+
13
+ One instance per request — never shared across requests or threads.
14
+ Interceptors stash signals here (PII findings, cache decisions, risk
15
+ scores) for the audit interceptor to collect at the end of the chain.
16
+ """
17
+
18
+ trace_id: str
19
+ agent_id: str | None = None
20
+ parent_trace_id: str | None = None
21
+ session_id: str | None = None
22
+ dry_run: bool = False
23
+
24
+ # Signals accumulated by interceptors during the request.
25
+ interceptors_fired: list[str] = field(default_factory=list)
26
+ pii_entity_types: list[str] = field(default_factory=list)
27
+ pii_entity_counts: dict[str, int] = field(default_factory=dict)
28
+ cache_hit: bool = False
29
+ cache_type: str | None = None
30
+ risk_score: float | None = None
31
+ guardrail_outcome: str | None = None
32
+
33
+ # Arbitrary inter-interceptor state (e.g. PII replacement map for restore).
34
+ state: dict[str, Any] = field(default_factory=dict)
35
+
36
+ def mark_fired(self, name: str) -> None:
37
+ if name not in self.interceptors_fired:
38
+ self.interceptors_fired.append(name)
39
+
40
+ def record_pii(self, entity_types: list[str]) -> None:
41
+ for et in entity_types:
42
+ self.pii_entity_counts[et] = self.pii_entity_counts.get(et, 0) + 1
43
+ if et not in self.pii_entity_types:
44
+ self.pii_entity_types.append(et)
@@ -0,0 +1,53 @@
1
+ """Gavio exception hierarchy.
2
+
3
+ All Gavio errors derive from :class:`GavioError` so callers can catch the
4
+ whole family with a single ``except``.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+
10
+ class GavioError(Exception):
11
+ """Base class for every error raised by Gavio."""
12
+
13
+
14
+ class ConfigurationError(GavioError):
15
+ """Raised when the gateway is misconfigured (e.g. no provider set)."""
16
+
17
+
18
+ class ProviderError(GavioError):
19
+ """Base class for provider-adapter failures."""
20
+
21
+
22
+ class ProviderUnavailableError(ProviderError):
23
+ """The provider could not be reached (network / health-check failure)."""
24
+
25
+
26
+ class RateLimitError(ProviderError):
27
+ """The provider returned a rate-limit (HTTP 429) signal."""
28
+
29
+
30
+ class ServerError(ProviderError):
31
+ """The provider returned a 5xx server error."""
32
+
33
+
34
+ class TimeoutError(ProviderError): # noqa: A001 - intentional domain name
35
+ """A request exceeded its configured timeout."""
36
+
37
+
38
+ class PiiBlockedError(GavioError):
39
+ """PiiGuard is in BLOCK mode and detected PII in the request."""
40
+
41
+ def __init__(self, entity_types: list[str]) -> None:
42
+ self.entity_types = entity_types
43
+ super().__init__(
44
+ f"Request blocked: PII detected ({', '.join(sorted(set(entity_types)))})"
45
+ )
46
+
47
+
48
+ class BudgetExceededError(GavioError):
49
+ """A hard budget cap was exceeded. Never swallow this — surface to user."""
50
+
51
+
52
+ class GuardrailViolationError(GavioError):
53
+ """Output failed a guardrail validator with on_failure='error'."""