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
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic-based schema validation for Proxilion.
|
|
3
|
+
|
|
4
|
+
This module provides optional Pydantic integration for richer
|
|
5
|
+
validation capabilities. It requires the pydantic package to be installed.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
from proxilion.validation.schema import (
|
|
14
|
+
ParameterSchema,
|
|
15
|
+
RiskLevel,
|
|
16
|
+
SchemaValidator,
|
|
17
|
+
ToolSchema,
|
|
18
|
+
ValidationResult,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Check if Pydantic is available
|
|
27
|
+
try:
|
|
28
|
+
from pydantic import BaseModel, ValidationError, create_model
|
|
29
|
+
from pydantic.fields import FieldInfo
|
|
30
|
+
from pydantic_core import PydanticUndefined
|
|
31
|
+
HAS_PYDANTIC = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
HAS_PYDANTIC = False
|
|
34
|
+
BaseModel = None # type: ignore
|
|
35
|
+
ValidationError = None # type: ignore
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PydanticSchemaValidator(SchemaValidator):
|
|
39
|
+
"""
|
|
40
|
+
Schema validator using Pydantic for rich validation.
|
|
41
|
+
|
|
42
|
+
This validator extends SchemaValidator to use Pydantic models
|
|
43
|
+
for validation, providing:
|
|
44
|
+
- Automatic type coercion
|
|
45
|
+
- Rich validation error messages
|
|
46
|
+
- JSON Schema generation
|
|
47
|
+
- Nested model validation
|
|
48
|
+
|
|
49
|
+
Requires: pip install proxilion[pydantic]
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> from pydantic import BaseModel
|
|
53
|
+
>>>
|
|
54
|
+
>>> class CalculatorInput(BaseModel):
|
|
55
|
+
... operation: str
|
|
56
|
+
... a: float
|
|
57
|
+
... b: float
|
|
58
|
+
>>>
|
|
59
|
+
>>> validator = PydanticSchemaValidator()
|
|
60
|
+
>>> validator.register_pydantic_model("calculator", CalculatorInput)
|
|
61
|
+
>>> result = validator.validate("calculator", {"operation": "add", "a": 5, "b": 3})
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, strict_mode: bool = False) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Initialize the Pydantic schema validator.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
strict_mode: If True, reject unknown parameters.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ImportError: If pydantic is not installed.
|
|
73
|
+
"""
|
|
74
|
+
if not HAS_PYDANTIC:
|
|
75
|
+
raise ImportError(
|
|
76
|
+
"Pydantic is not installed. Install with: pip install proxilion[pydantic]"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
super().__init__(strict_mode)
|
|
80
|
+
self._pydantic_models: dict[str, type[BaseModel]] = {}
|
|
81
|
+
|
|
82
|
+
def register_pydantic_model(
|
|
83
|
+
self,
|
|
84
|
+
tool_name: str,
|
|
85
|
+
model: type[BaseModel],
|
|
86
|
+
risk_level: RiskLevel = "low",
|
|
87
|
+
sensitive_fields: list[str] | None = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Register a Pydantic model as the schema for a tool.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
tool_name: The tool name.
|
|
94
|
+
model: The Pydantic model class.
|
|
95
|
+
risk_level: Risk level for the tool.
|
|
96
|
+
sensitive_fields: List of field names that contain sensitive data.
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
>>> class QueryInput(BaseModel):
|
|
100
|
+
... query: str
|
|
101
|
+
... limit: int = 100
|
|
102
|
+
>>>
|
|
103
|
+
>>> validator.register_pydantic_model("database_query", QueryInput)
|
|
104
|
+
"""
|
|
105
|
+
self._pydantic_models[tool_name] = model
|
|
106
|
+
|
|
107
|
+
# Also create a ToolSchema for compatibility
|
|
108
|
+
schema = self._pydantic_model_to_tool_schema(
|
|
109
|
+
model, tool_name, risk_level, sensitive_fields or []
|
|
110
|
+
)
|
|
111
|
+
self.register_schema(tool_name, schema)
|
|
112
|
+
|
|
113
|
+
logger.debug(f"Registered Pydantic model for tool '{tool_name}'")
|
|
114
|
+
|
|
115
|
+
def _pydantic_model_to_tool_schema(
|
|
116
|
+
self,
|
|
117
|
+
model: type[BaseModel],
|
|
118
|
+
tool_name: str,
|
|
119
|
+
risk_level: RiskLevel,
|
|
120
|
+
sensitive_fields: list[str],
|
|
121
|
+
) -> ToolSchema:
|
|
122
|
+
"""Convert a Pydantic model to a ToolSchema."""
|
|
123
|
+
parameters: dict[str, ParameterSchema] = {}
|
|
124
|
+
required_params: list[str] = []
|
|
125
|
+
|
|
126
|
+
# Get field information from the model
|
|
127
|
+
for field_name, field_info in model.model_fields.items():
|
|
128
|
+
# Determine type
|
|
129
|
+
annotation = field_info.annotation
|
|
130
|
+
param_type = self._annotation_to_type(annotation)
|
|
131
|
+
|
|
132
|
+
# Check if required
|
|
133
|
+
is_required = field_info.is_required()
|
|
134
|
+
if is_required:
|
|
135
|
+
required_params.append(field_name)
|
|
136
|
+
|
|
137
|
+
# Get default value
|
|
138
|
+
default = None
|
|
139
|
+
if not is_required and field_info.default is not PydanticUndefined:
|
|
140
|
+
default = field_info.default
|
|
141
|
+
|
|
142
|
+
# Build constraints from Pydantic field info
|
|
143
|
+
constraints = self._field_info_to_constraints(field_info)
|
|
144
|
+
|
|
145
|
+
parameters[field_name] = ParameterSchema(
|
|
146
|
+
name=field_name,
|
|
147
|
+
type=param_type,
|
|
148
|
+
description=field_info.description or "",
|
|
149
|
+
constraints=constraints,
|
|
150
|
+
sensitive=field_name in sensitive_fields,
|
|
151
|
+
default=default,
|
|
152
|
+
required=is_required,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return ToolSchema(
|
|
156
|
+
name=tool_name,
|
|
157
|
+
description=model.__doc__ or "",
|
|
158
|
+
parameters=parameters,
|
|
159
|
+
required_parameters=required_params,
|
|
160
|
+
risk_level=risk_level,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def _annotation_to_type(self, annotation: Any) -> str:
|
|
164
|
+
"""Convert a type annotation to a schema type string."""
|
|
165
|
+
if annotation is None:
|
|
166
|
+
return "any"
|
|
167
|
+
|
|
168
|
+
# Handle Optional and Union types
|
|
169
|
+
origin = getattr(annotation, "__origin__", None)
|
|
170
|
+
if origin is not None:
|
|
171
|
+
# Get the first non-None type from Union
|
|
172
|
+
args = getattr(annotation, "__args__", ())
|
|
173
|
+
for arg in args:
|
|
174
|
+
if arg is not type(None):
|
|
175
|
+
return self._annotation_to_type(arg)
|
|
176
|
+
|
|
177
|
+
# Simple types
|
|
178
|
+
if annotation is str:
|
|
179
|
+
return "str"
|
|
180
|
+
elif annotation is int:
|
|
181
|
+
return "int"
|
|
182
|
+
elif annotation is float:
|
|
183
|
+
return "float"
|
|
184
|
+
elif annotation is bool:
|
|
185
|
+
return "bool"
|
|
186
|
+
elif annotation is list or origin is list:
|
|
187
|
+
return "list"
|
|
188
|
+
elif annotation is dict or origin is dict:
|
|
189
|
+
return "dict"
|
|
190
|
+
|
|
191
|
+
return "any"
|
|
192
|
+
|
|
193
|
+
def _field_info_to_constraints(self, field_info: FieldInfo) -> dict[str, Any]:
|
|
194
|
+
"""Extract constraints from Pydantic FieldInfo."""
|
|
195
|
+
constraints: dict[str, Any] = {}
|
|
196
|
+
|
|
197
|
+
# Get metadata from field_info
|
|
198
|
+
metadata = getattr(field_info, "metadata", [])
|
|
199
|
+
for item in metadata:
|
|
200
|
+
# Handle Pydantic constraints (Gt, Lt, Ge, Le, etc.)
|
|
201
|
+
if hasattr(item, "gt"):
|
|
202
|
+
constraints["min"] = item.gt
|
|
203
|
+
if hasattr(item, "ge"):
|
|
204
|
+
constraints["min"] = item.ge
|
|
205
|
+
if hasattr(item, "lt"):
|
|
206
|
+
constraints["max"] = item.lt
|
|
207
|
+
if hasattr(item, "le"):
|
|
208
|
+
constraints["max"] = item.le
|
|
209
|
+
if hasattr(item, "min_length"):
|
|
210
|
+
constraints["min_length"] = item.min_length
|
|
211
|
+
if hasattr(item, "max_length"):
|
|
212
|
+
constraints["max_length"] = item.max_length
|
|
213
|
+
if hasattr(item, "pattern"):
|
|
214
|
+
constraints["pattern"] = item.pattern
|
|
215
|
+
|
|
216
|
+
return constraints
|
|
217
|
+
|
|
218
|
+
def validate(
|
|
219
|
+
self,
|
|
220
|
+
tool_name: str,
|
|
221
|
+
arguments: dict[str, Any],
|
|
222
|
+
) -> ValidationResult:
|
|
223
|
+
"""
|
|
224
|
+
Validate arguments using Pydantic model if available.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
tool_name: The tool name.
|
|
228
|
+
arguments: The arguments to validate.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
ValidationResult with validation status.
|
|
232
|
+
"""
|
|
233
|
+
# Check if we have a Pydantic model for this tool
|
|
234
|
+
model = self._pydantic_models.get(tool_name)
|
|
235
|
+
|
|
236
|
+
if model is not None:
|
|
237
|
+
return self._validate_with_pydantic(model, tool_name, arguments)
|
|
238
|
+
|
|
239
|
+
# Fall back to standard validation
|
|
240
|
+
return super().validate(tool_name, arguments)
|
|
241
|
+
|
|
242
|
+
def _validate_with_pydantic(
|
|
243
|
+
self,
|
|
244
|
+
model: type[BaseModel],
|
|
245
|
+
tool_name: str,
|
|
246
|
+
arguments: dict[str, Any],
|
|
247
|
+
) -> ValidationResult:
|
|
248
|
+
"""Validate using a Pydantic model."""
|
|
249
|
+
try:
|
|
250
|
+
# Validate and get the model instance
|
|
251
|
+
validated = model.model_validate(arguments)
|
|
252
|
+
|
|
253
|
+
# Convert back to dict for sanitized output
|
|
254
|
+
sanitized = validated.model_dump()
|
|
255
|
+
|
|
256
|
+
return ValidationResult.success(sanitized=sanitized)
|
|
257
|
+
|
|
258
|
+
except ValidationError as e:
|
|
259
|
+
# Convert Pydantic errors to our format
|
|
260
|
+
errors = []
|
|
261
|
+
for error in e.errors():
|
|
262
|
+
loc = ".".join(str(part) for part in error["loc"])
|
|
263
|
+
msg = error["msg"]
|
|
264
|
+
errors.append(f"Parameter '{loc}': {msg}")
|
|
265
|
+
|
|
266
|
+
return ValidationResult.failure(errors)
|
|
267
|
+
|
|
268
|
+
def get_json_schema(self, tool_name: str) -> dict[str, Any] | None:
|
|
269
|
+
"""
|
|
270
|
+
Get JSON Schema for a tool.
|
|
271
|
+
|
|
272
|
+
Pydantic can generate JSON Schema from models, which is useful
|
|
273
|
+
for documentation and OpenAI function calling.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
tool_name: The tool name.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
JSON Schema dict or None if no model registered.
|
|
280
|
+
"""
|
|
281
|
+
model = self._pydantic_models.get(tool_name)
|
|
282
|
+
if model is None:
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
return model.model_json_schema()
|
|
286
|
+
|
|
287
|
+
def create_model_from_schema(
|
|
288
|
+
self,
|
|
289
|
+
schema: ToolSchema,
|
|
290
|
+
) -> type[BaseModel]:
|
|
291
|
+
"""
|
|
292
|
+
Create a Pydantic model from a ToolSchema.
|
|
293
|
+
|
|
294
|
+
This is useful when you have a ToolSchema but want to use
|
|
295
|
+
Pydantic's validation.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
schema: The ToolSchema to convert.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
A dynamically created Pydantic model class.
|
|
302
|
+
"""
|
|
303
|
+
field_definitions: dict[str, Any] = {}
|
|
304
|
+
|
|
305
|
+
for param_name, param_schema in schema.parameters.items():
|
|
306
|
+
# Get Python type
|
|
307
|
+
python_type = self._schema_type_to_python(param_schema.type)
|
|
308
|
+
|
|
309
|
+
# Build field info
|
|
310
|
+
if param_schema.required:
|
|
311
|
+
field_definitions[param_name] = (python_type, ...)
|
|
312
|
+
else:
|
|
313
|
+
field_definitions[param_name] = (python_type, param_schema.default)
|
|
314
|
+
|
|
315
|
+
# Create the model dynamically
|
|
316
|
+
model = create_model(
|
|
317
|
+
f"{schema.name}Input",
|
|
318
|
+
**field_definitions,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
return model
|
|
322
|
+
|
|
323
|
+
def _schema_type_to_python(self, type_str: str) -> type:
|
|
324
|
+
"""Convert schema type string to Python type."""
|
|
325
|
+
type_map = {
|
|
326
|
+
"str": str,
|
|
327
|
+
"int": int,
|
|
328
|
+
"float": float,
|
|
329
|
+
"bool": bool,
|
|
330
|
+
"list": list,
|
|
331
|
+
"dict": dict,
|
|
332
|
+
"any": Any,
|
|
333
|
+
}
|
|
334
|
+
return type_map.get(type_str, Any)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def create_pydantic_validator() -> PydanticSchemaValidator | None:
|
|
338
|
+
"""
|
|
339
|
+
Create a PydanticSchemaValidator if Pydantic is available.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
PydanticSchemaValidator instance or None if Pydantic not installed.
|
|
343
|
+
"""
|
|
344
|
+
if not HAS_PYDANTIC:
|
|
345
|
+
logger.warning(
|
|
346
|
+
"Pydantic not installed. Using standard SchemaValidator. "
|
|
347
|
+
"Install with: pip install proxilion[pydantic]"
|
|
348
|
+
)
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
return PydanticSchemaValidator()
|