proxilion 0.0.1__py3-none-any.whl
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.
- proxilion/__init__.py +136 -0
- proxilion/audit/__init__.py +133 -0
- proxilion/audit/base_exporters.py +527 -0
- proxilion/audit/compliance/__init__.py +130 -0
- proxilion/audit/compliance/base.py +457 -0
- proxilion/audit/compliance/eu_ai_act.py +603 -0
- proxilion/audit/compliance/iso27001.py +544 -0
- proxilion/audit/compliance/soc2.py +491 -0
- proxilion/audit/events.py +493 -0
- proxilion/audit/explainability.py +1173 -0
- proxilion/audit/exporters/__init__.py +58 -0
- proxilion/audit/exporters/aws_s3.py +636 -0
- proxilion/audit/exporters/azure_storage.py +608 -0
- proxilion/audit/exporters/cloud_base.py +468 -0
- proxilion/audit/exporters/gcp_storage.py +570 -0
- proxilion/audit/exporters/multi_exporter.py +498 -0
- proxilion/audit/hash_chain.py +652 -0
- proxilion/audit/logger.py +543 -0
- proxilion/caching/__init__.py +49 -0
- proxilion/caching/tool_cache.py +633 -0
- proxilion/context/__init__.py +73 -0
- proxilion/context/context_window.py +556 -0
- proxilion/context/message_history.py +505 -0
- proxilion/context/session.py +735 -0
- proxilion/contrib/__init__.py +51 -0
- proxilion/contrib/anthropic.py +609 -0
- proxilion/contrib/google.py +1012 -0
- proxilion/contrib/langchain.py +641 -0
- proxilion/contrib/mcp.py +893 -0
- proxilion/contrib/openai.py +646 -0
- proxilion/core.py +3058 -0
- proxilion/decorators.py +966 -0
- proxilion/engines/__init__.py +287 -0
- proxilion/engines/base.py +266 -0
- proxilion/engines/casbin_engine.py +412 -0
- proxilion/engines/opa_engine.py +493 -0
- proxilion/engines/simple.py +437 -0
- proxilion/exceptions.py +887 -0
- proxilion/guards/__init__.py +54 -0
- proxilion/guards/input_guard.py +522 -0
- proxilion/guards/output_guard.py +634 -0
- proxilion/observability/__init__.py +198 -0
- proxilion/observability/cost_tracker.py +866 -0
- proxilion/observability/hooks.py +683 -0
- proxilion/observability/metrics.py +798 -0
- proxilion/observability/session_cost_tracker.py +1063 -0
- proxilion/policies/__init__.py +67 -0
- proxilion/policies/base.py +304 -0
- proxilion/policies/builtin.py +486 -0
- proxilion/policies/registry.py +376 -0
- proxilion/providers/__init__.py +201 -0
- proxilion/providers/adapter.py +468 -0
- proxilion/providers/anthropic_adapter.py +330 -0
- proxilion/providers/gemini_adapter.py +391 -0
- proxilion/providers/openai_adapter.py +294 -0
- proxilion/py.typed +0 -0
- proxilion/resilience/__init__.py +81 -0
- proxilion/resilience/degradation.py +615 -0
- proxilion/resilience/fallback.py +555 -0
- proxilion/resilience/retry.py +554 -0
- proxilion/scheduling/__init__.py +57 -0
- proxilion/scheduling/priority_queue.py +419 -0
- proxilion/scheduling/scheduler.py +459 -0
- proxilion/security/__init__.py +244 -0
- proxilion/security/agent_trust.py +968 -0
- proxilion/security/behavioral_drift.py +794 -0
- proxilion/security/cascade_protection.py +869 -0
- proxilion/security/circuit_breaker.py +428 -0
- proxilion/security/cost_limiter.py +690 -0
- proxilion/security/idor_protection.py +460 -0
- proxilion/security/intent_capsule.py +849 -0
- proxilion/security/intent_validator.py +495 -0
- proxilion/security/memory_integrity.py +767 -0
- proxilion/security/rate_limiter.py +509 -0
- proxilion/security/scope_enforcer.py +680 -0
- proxilion/security/sequence_validator.py +636 -0
- proxilion/security/trust_boundaries.py +784 -0
- proxilion/streaming/__init__.py +70 -0
- proxilion/streaming/detector.py +761 -0
- proxilion/streaming/transformer.py +674 -0
- proxilion/timeouts/__init__.py +55 -0
- proxilion/timeouts/decorators.py +477 -0
- proxilion/timeouts/manager.py +545 -0
- proxilion/tools/__init__.py +69 -0
- proxilion/tools/decorators.py +493 -0
- proxilion/tools/registry.py +732 -0
- proxilion/types.py +339 -0
- proxilion/validation/__init__.py +93 -0
- proxilion/validation/pydantic_schema.py +351 -0
- proxilion/validation/schema.py +651 -0
- proxilion-0.0.1.dist-info/METADATA +872 -0
- proxilion-0.0.1.dist-info/RECORD +94 -0
- proxilion-0.0.1.dist-info/WHEEL +4 -0
- proxilion-0.0.1.dist-info/licenses/LICENSE +21 -0
proxilion/types.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core type definitions for Proxilion.
|
|
3
|
+
|
|
4
|
+
This module defines the fundamental data structures used throughout the SDK
|
|
5
|
+
for representing user context, agent context, tool call requests, authorization
|
|
6
|
+
results, and audit events.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import uuid
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _generate_event_id() -> str:
|
|
20
|
+
"""Generate a UUID v4 for event identification.
|
|
21
|
+
|
|
22
|
+
Note: UUID v7 would be ideal for time-ordering, but it's not available
|
|
23
|
+
in Python stdlib until 3.14. Using UUID v4 with explicit timestamps instead.
|
|
24
|
+
"""
|
|
25
|
+
return str(uuid.uuid4())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _utc_now() -> datetime:
|
|
29
|
+
"""Get current UTC time with timezone info."""
|
|
30
|
+
return datetime.now(timezone.utc)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class UserContext:
|
|
35
|
+
"""
|
|
36
|
+
Represents the authenticated user making a tool call request.
|
|
37
|
+
|
|
38
|
+
This context travels through the agent to the tool, ensuring that
|
|
39
|
+
authorization decisions are made based on the actual user's identity
|
|
40
|
+
and permissions, not the agent's service account.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
user_id: Unique identifier for the user (e.g., from your auth system).
|
|
44
|
+
roles: List of role names assigned to the user (e.g., ["analyst", "viewer"]).
|
|
45
|
+
session_id: Optional session identifier for tracking request context.
|
|
46
|
+
attributes: Additional custom attributes for policy decisions
|
|
47
|
+
(e.g., {"department": "engineering", "clearance_level": 3}).
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> user = UserContext(
|
|
51
|
+
... user_id="user_123",
|
|
52
|
+
... roles=["analyst", "viewer"],
|
|
53
|
+
... session_id="sess_abc",
|
|
54
|
+
... attributes={"department": "engineering"}
|
|
55
|
+
... )
|
|
56
|
+
"""
|
|
57
|
+
user_id: str
|
|
58
|
+
roles: list[str] = field(default_factory=list)
|
|
59
|
+
session_id: str | None = None
|
|
60
|
+
attributes: dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
|
|
62
|
+
def has_role(self, role: str) -> bool:
|
|
63
|
+
"""Check if user has a specific role."""
|
|
64
|
+
return role in self.roles
|
|
65
|
+
|
|
66
|
+
def has_any_role(self, roles: list[str]) -> bool:
|
|
67
|
+
"""Check if user has any of the specified roles."""
|
|
68
|
+
return any(role in self.roles for role in roles)
|
|
69
|
+
|
|
70
|
+
def has_all_roles(self, roles: list[str]) -> bool:
|
|
71
|
+
"""Check if user has all of the specified roles."""
|
|
72
|
+
return all(role in self.roles for role in roles)
|
|
73
|
+
|
|
74
|
+
def get_attribute(self, key: str, default: Any = None) -> Any:
|
|
75
|
+
"""Get a user attribute with optional default."""
|
|
76
|
+
return self.attributes.get(key, default)
|
|
77
|
+
|
|
78
|
+
def to_dict(self) -> dict[str, Any]:
|
|
79
|
+
"""Convert to dictionary for serialization."""
|
|
80
|
+
return {
|
|
81
|
+
"user_id": self.user_id,
|
|
82
|
+
"roles": self.roles,
|
|
83
|
+
"session_id": self.session_id,
|
|
84
|
+
"attributes": self.attributes,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True)
|
|
89
|
+
class AgentContext:
|
|
90
|
+
"""
|
|
91
|
+
Represents the AI agent making tool calls on behalf of a user.
|
|
92
|
+
|
|
93
|
+
The agent context helps track which agent is operating and can be used
|
|
94
|
+
to implement agent-level security policies (e.g., limiting which agents
|
|
95
|
+
can use sensitive tools).
|
|
96
|
+
|
|
97
|
+
Attributes:
|
|
98
|
+
agent_id: Unique identifier for the agent instance.
|
|
99
|
+
capabilities: List of capability names the agent is allowed to use.
|
|
100
|
+
trust_score: Float between 0-1 indicating agent trust level.
|
|
101
|
+
Lower scores may trigger additional verification.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
>>> agent = AgentContext(
|
|
105
|
+
... agent_id="agent_openai_gpt4",
|
|
106
|
+
... capabilities=["read_files", "search"],
|
|
107
|
+
... trust_score=0.8
|
|
108
|
+
... )
|
|
109
|
+
"""
|
|
110
|
+
agent_id: str
|
|
111
|
+
capabilities: list[str] = field(default_factory=list)
|
|
112
|
+
trust_score: float = 1.0
|
|
113
|
+
|
|
114
|
+
def __post_init__(self) -> None:
|
|
115
|
+
"""Validate trust_score is within bounds."""
|
|
116
|
+
if not 0.0 <= self.trust_score <= 1.0:
|
|
117
|
+
raise ValueError(f"trust_score must be between 0 and 1, got {self.trust_score}")
|
|
118
|
+
|
|
119
|
+
def has_capability(self, capability: str) -> bool:
|
|
120
|
+
"""Check if agent has a specific capability."""
|
|
121
|
+
return capability in self.capabilities
|
|
122
|
+
|
|
123
|
+
def is_high_trust(self, threshold: float = 0.8) -> bool:
|
|
124
|
+
"""Check if agent meets high trust threshold."""
|
|
125
|
+
return self.trust_score >= threshold
|
|
126
|
+
|
|
127
|
+
def to_dict(self) -> dict[str, Any]:
|
|
128
|
+
"""Convert to dictionary for serialization."""
|
|
129
|
+
return {
|
|
130
|
+
"agent_id": self.agent_id,
|
|
131
|
+
"capabilities": self.capabilities,
|
|
132
|
+
"trust_score": self.trust_score,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass(frozen=True)
|
|
137
|
+
class ToolCallRequest:
|
|
138
|
+
"""
|
|
139
|
+
Represents a request to execute a tool.
|
|
140
|
+
|
|
141
|
+
This captures all the information about what tool is being called
|
|
142
|
+
and with what arguments, enabling validation and authorization checks.
|
|
143
|
+
|
|
144
|
+
Attributes:
|
|
145
|
+
tool_name: Name of the tool being invoked.
|
|
146
|
+
arguments: Dictionary of arguments passed to the tool.
|
|
147
|
+
timestamp: When the request was made (UTC).
|
|
148
|
+
|
|
149
|
+
Example:
|
|
150
|
+
>>> request = ToolCallRequest(
|
|
151
|
+
... tool_name="database_query",
|
|
152
|
+
... arguments={"query": "SELECT * FROM users", "limit": 100}
|
|
153
|
+
... )
|
|
154
|
+
"""
|
|
155
|
+
tool_name: str
|
|
156
|
+
arguments: dict[str, Any] = field(default_factory=dict)
|
|
157
|
+
timestamp: datetime = field(default_factory=_utc_now)
|
|
158
|
+
|
|
159
|
+
def get_argument(self, key: str, default: Any = None) -> Any:
|
|
160
|
+
"""Get an argument value with optional default."""
|
|
161
|
+
return self.arguments.get(key, default)
|
|
162
|
+
|
|
163
|
+
def to_dict(self) -> dict[str, Any]:
|
|
164
|
+
"""Convert to dictionary for serialization."""
|
|
165
|
+
return {
|
|
166
|
+
"tool_name": self.tool_name,
|
|
167
|
+
"arguments": self.arguments,
|
|
168
|
+
"timestamp": self.timestamp.isoformat(),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass(frozen=True)
|
|
173
|
+
class AuthorizationResult:
|
|
174
|
+
"""
|
|
175
|
+
Result of an authorization check.
|
|
176
|
+
|
|
177
|
+
Captures whether the action was allowed, the reason for the decision,
|
|
178
|
+
and which policies were evaluated to reach the decision.
|
|
179
|
+
|
|
180
|
+
Attributes:
|
|
181
|
+
allowed: Whether the action is authorized.
|
|
182
|
+
reason: Human-readable explanation of the decision.
|
|
183
|
+
policies_evaluated: List of policy names that were checked.
|
|
184
|
+
metadata: Additional information about the decision
|
|
185
|
+
(e.g., rate limit remaining, matched rules).
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
>>> result = AuthorizationResult(
|
|
189
|
+
... allowed=True,
|
|
190
|
+
... reason="User has 'analyst' role",
|
|
191
|
+
... policies_evaluated=["DatabaseQueryPolicy"]
|
|
192
|
+
... )
|
|
193
|
+
"""
|
|
194
|
+
allowed: bool
|
|
195
|
+
reason: str | None = None
|
|
196
|
+
policies_evaluated: list[str] = field(default_factory=list)
|
|
197
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def allow(cls, reason: str | None = None,
|
|
201
|
+
policies: list[str] | None = None,
|
|
202
|
+
metadata: dict[str, Any] | None = None) -> AuthorizationResult:
|
|
203
|
+
"""Create an allowed result."""
|
|
204
|
+
return cls(
|
|
205
|
+
allowed=True,
|
|
206
|
+
reason=reason,
|
|
207
|
+
policies_evaluated=policies or [],
|
|
208
|
+
metadata=metadata or {},
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
@classmethod
|
|
212
|
+
def deny(cls, reason: str,
|
|
213
|
+
policies: list[str] | None = None,
|
|
214
|
+
metadata: dict[str, Any] | None = None) -> AuthorizationResult:
|
|
215
|
+
"""Create a denied result."""
|
|
216
|
+
return cls(
|
|
217
|
+
allowed=False,
|
|
218
|
+
reason=reason,
|
|
219
|
+
policies_evaluated=policies or [],
|
|
220
|
+
metadata=metadata or {},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def to_dict(self) -> dict[str, Any]:
|
|
224
|
+
"""Convert to dictionary for serialization."""
|
|
225
|
+
return {
|
|
226
|
+
"allowed": self.allowed,
|
|
227
|
+
"reason": self.reason,
|
|
228
|
+
"policies_evaluated": self.policies_evaluated,
|
|
229
|
+
"metadata": self.metadata,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass
|
|
234
|
+
class AuditEvent:
|
|
235
|
+
"""
|
|
236
|
+
A tamper-evident audit log entry for a tool call authorization decision.
|
|
237
|
+
|
|
238
|
+
Each event is linked to the previous event via a hash chain, providing
|
|
239
|
+
cryptographic proof of log integrity. Any modification to historical
|
|
240
|
+
events will break the hash chain and be detectable.
|
|
241
|
+
|
|
242
|
+
Attributes:
|
|
243
|
+
event_id: Unique identifier for this event.
|
|
244
|
+
timestamp: When the event occurred (UTC).
|
|
245
|
+
sequence_number: Monotonically increasing counter for ordering.
|
|
246
|
+
user_context: The user who initiated the tool call.
|
|
247
|
+
agent_context: The agent that made the request (optional).
|
|
248
|
+
tool_call: The tool call request details.
|
|
249
|
+
authorization_result: The authorization decision.
|
|
250
|
+
execution_result: Summary of tool execution (optional, no sensitive data).
|
|
251
|
+
previous_hash: Hash of the previous event in the chain.
|
|
252
|
+
event_hash: Hash of this event (computed after creation).
|
|
253
|
+
|
|
254
|
+
Example:
|
|
255
|
+
>>> event = AuditEvent(
|
|
256
|
+
... user_context=user,
|
|
257
|
+
... tool_call=request,
|
|
258
|
+
... authorization_result=result,
|
|
259
|
+
... sequence_number=1,
|
|
260
|
+
... previous_hash="GENESIS"
|
|
261
|
+
... )
|
|
262
|
+
>>> event.compute_hash()
|
|
263
|
+
"""
|
|
264
|
+
user_context: UserContext
|
|
265
|
+
tool_call: ToolCallRequest
|
|
266
|
+
authorization_result: AuthorizationResult
|
|
267
|
+
sequence_number: int
|
|
268
|
+
previous_hash: str
|
|
269
|
+
event_id: str = field(default_factory=_generate_event_id)
|
|
270
|
+
timestamp: datetime = field(default_factory=_utc_now)
|
|
271
|
+
agent_context: AgentContext | None = None
|
|
272
|
+
execution_result: dict[str, Any] | None = None
|
|
273
|
+
event_hash: str = ""
|
|
274
|
+
|
|
275
|
+
def _canonical_json(self) -> str:
|
|
276
|
+
"""
|
|
277
|
+
Generate canonical JSON representation for hashing.
|
|
278
|
+
|
|
279
|
+
Uses sorted keys and consistent formatting to ensure the same
|
|
280
|
+
data always produces the same hash.
|
|
281
|
+
"""
|
|
282
|
+
data = {
|
|
283
|
+
"event_id": self.event_id,
|
|
284
|
+
"timestamp": self.timestamp.isoformat(),
|
|
285
|
+
"sequence_number": self.sequence_number,
|
|
286
|
+
"user_context": self.user_context.to_dict(),
|
|
287
|
+
"agent_context": self.agent_context.to_dict() if self.agent_context else None,
|
|
288
|
+
"tool_call": self.tool_call.to_dict(),
|
|
289
|
+
"authorization_result": self.authorization_result.to_dict(),
|
|
290
|
+
"execution_result": self.execution_result,
|
|
291
|
+
"previous_hash": self.previous_hash,
|
|
292
|
+
}
|
|
293
|
+
return json.dumps(data, sort_keys=True, separators=(",", ":"))
|
|
294
|
+
|
|
295
|
+
def compute_hash(self) -> str:
|
|
296
|
+
"""
|
|
297
|
+
Compute and set the event hash using SHA-256.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
The computed hash as a hex string prefixed with 'sha256:'.
|
|
301
|
+
"""
|
|
302
|
+
canonical = self._canonical_json()
|
|
303
|
+
hash_bytes = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
304
|
+
self.event_hash = f"sha256:{hash_bytes}"
|
|
305
|
+
return self.event_hash
|
|
306
|
+
|
|
307
|
+
def verify_hash(self) -> bool:
|
|
308
|
+
"""
|
|
309
|
+
Verify that the stored hash matches the computed hash.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
True if the hash is valid, False if tampered.
|
|
313
|
+
"""
|
|
314
|
+
if not self.event_hash:
|
|
315
|
+
return False
|
|
316
|
+
expected = self.compute_hash()
|
|
317
|
+
# Restore the original hash since compute_hash modifies it
|
|
318
|
+
current = self.event_hash
|
|
319
|
+
self.event_hash = current
|
|
320
|
+
return current == expected
|
|
321
|
+
|
|
322
|
+
def to_dict(self) -> dict[str, Any]:
|
|
323
|
+
"""Convert to dictionary for serialization."""
|
|
324
|
+
return {
|
|
325
|
+
"event_id": self.event_id,
|
|
326
|
+
"timestamp": self.timestamp.isoformat(),
|
|
327
|
+
"sequence_number": self.sequence_number,
|
|
328
|
+
"user_context": self.user_context.to_dict(),
|
|
329
|
+
"agent_context": self.agent_context.to_dict() if self.agent_context else None,
|
|
330
|
+
"tool_call": self.tool_call.to_dict(),
|
|
331
|
+
"authorization_result": self.authorization_result.to_dict(),
|
|
332
|
+
"execution_result": self.execution_result,
|
|
333
|
+
"previous_hash": self.previous_hash,
|
|
334
|
+
"event_hash": self.event_hash,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
def to_json(self) -> str:
|
|
338
|
+
"""Convert to JSON string with canonical formatting."""
|
|
339
|
+
return json.dumps(self.to_dict(), sort_keys=True, indent=2)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schema validation system for Proxilion.
|
|
3
|
+
|
|
4
|
+
This module provides tools for validating LLM tool call arguments
|
|
5
|
+
against defined schemas. It includes security validations for
|
|
6
|
+
path traversal, SQL injection, and IDOR protection.
|
|
7
|
+
|
|
8
|
+
Quick Start:
|
|
9
|
+
>>> from proxilion.validation import SchemaValidator, ToolSchema, ParameterSchema
|
|
10
|
+
>>>
|
|
11
|
+
>>> validator = SchemaValidator()
|
|
12
|
+
>>> schema = ToolSchema(
|
|
13
|
+
... name="file_read",
|
|
14
|
+
... description="Read a file",
|
|
15
|
+
... parameters={
|
|
16
|
+
... "path": ParameterSchema(
|
|
17
|
+
... name="path",
|
|
18
|
+
... type="str",
|
|
19
|
+
... constraints={"allow_path_traversal": False},
|
|
20
|
+
... ),
|
|
21
|
+
... },
|
|
22
|
+
... required_parameters=["path"],
|
|
23
|
+
... risk_level="medium",
|
|
24
|
+
... )
|
|
25
|
+
>>> validator.register_schema("file_read", schema)
|
|
26
|
+
>>> result = validator.validate("file_read", {"path": "/safe/path.txt"})
|
|
27
|
+
>>> print(result.valid)
|
|
28
|
+
True
|
|
29
|
+
|
|
30
|
+
Security Features:
|
|
31
|
+
- Path traversal detection: Rejects ".." and encoded variants
|
|
32
|
+
- SQL injection detection: Detects common SQL injection patterns
|
|
33
|
+
- Object ID validation: Validates UUID, numeric, and alphanumeric IDs
|
|
34
|
+
- Parameter constraints: min, max, pattern, enum, length limits
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from proxilion.validation.schema import (
|
|
38
|
+
ParameterSchema,
|
|
39
|
+
RiskLevel,
|
|
40
|
+
SchemaValidator,
|
|
41
|
+
ToolSchema,
|
|
42
|
+
ValidationResult,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Optional Pydantic support
|
|
46
|
+
try:
|
|
47
|
+
from proxilion.validation.pydantic_schema import (
|
|
48
|
+
PydanticSchemaValidator,
|
|
49
|
+
create_pydantic_validator,
|
|
50
|
+
)
|
|
51
|
+
HAS_PYDANTIC = True
|
|
52
|
+
except ImportError:
|
|
53
|
+
HAS_PYDANTIC = False
|
|
54
|
+
PydanticSchemaValidator = None # type: ignore
|
|
55
|
+
create_pydantic_validator = None # type: ignore
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def create_validator(use_pydantic: bool = False) -> SchemaValidator:
|
|
59
|
+
"""
|
|
60
|
+
Create a schema validator.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
use_pydantic: If True, try to create a PydanticSchemaValidator.
|
|
64
|
+
Falls back to SchemaValidator if Pydantic is not installed.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
A SchemaValidator or PydanticSchemaValidator instance.
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
>>> validator = create_validator(use_pydantic=True)
|
|
71
|
+
"""
|
|
72
|
+
if use_pydantic and HAS_PYDANTIC and create_pydantic_validator is not None:
|
|
73
|
+
pydantic_validator = create_pydantic_validator()
|
|
74
|
+
if pydantic_validator is not None:
|
|
75
|
+
return pydantic_validator
|
|
76
|
+
|
|
77
|
+
return SchemaValidator()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
__all__ = [
|
|
81
|
+
# Core classes
|
|
82
|
+
"ToolSchema",
|
|
83
|
+
"ParameterSchema",
|
|
84
|
+
"SchemaValidator",
|
|
85
|
+
"ValidationResult",
|
|
86
|
+
"RiskLevel",
|
|
87
|
+
# Factory
|
|
88
|
+
"create_validator",
|
|
89
|
+
# Pydantic (optional)
|
|
90
|
+
"PydanticSchemaValidator",
|
|
91
|
+
"create_pydantic_validator",
|
|
92
|
+
"HAS_PYDANTIC",
|
|
93
|
+
]
|