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.
Files changed (94) hide show
  1. proxilion/__init__.py +136 -0
  2. proxilion/audit/__init__.py +133 -0
  3. proxilion/audit/base_exporters.py +527 -0
  4. proxilion/audit/compliance/__init__.py +130 -0
  5. proxilion/audit/compliance/base.py +457 -0
  6. proxilion/audit/compliance/eu_ai_act.py +603 -0
  7. proxilion/audit/compliance/iso27001.py +544 -0
  8. proxilion/audit/compliance/soc2.py +491 -0
  9. proxilion/audit/events.py +493 -0
  10. proxilion/audit/explainability.py +1173 -0
  11. proxilion/audit/exporters/__init__.py +58 -0
  12. proxilion/audit/exporters/aws_s3.py +636 -0
  13. proxilion/audit/exporters/azure_storage.py +608 -0
  14. proxilion/audit/exporters/cloud_base.py +468 -0
  15. proxilion/audit/exporters/gcp_storage.py +570 -0
  16. proxilion/audit/exporters/multi_exporter.py +498 -0
  17. proxilion/audit/hash_chain.py +652 -0
  18. proxilion/audit/logger.py +543 -0
  19. proxilion/caching/__init__.py +49 -0
  20. proxilion/caching/tool_cache.py +633 -0
  21. proxilion/context/__init__.py +73 -0
  22. proxilion/context/context_window.py +556 -0
  23. proxilion/context/message_history.py +505 -0
  24. proxilion/context/session.py +735 -0
  25. proxilion/contrib/__init__.py +51 -0
  26. proxilion/contrib/anthropic.py +609 -0
  27. proxilion/contrib/google.py +1012 -0
  28. proxilion/contrib/langchain.py +641 -0
  29. proxilion/contrib/mcp.py +893 -0
  30. proxilion/contrib/openai.py +646 -0
  31. proxilion/core.py +3058 -0
  32. proxilion/decorators.py +966 -0
  33. proxilion/engines/__init__.py +287 -0
  34. proxilion/engines/base.py +266 -0
  35. proxilion/engines/casbin_engine.py +412 -0
  36. proxilion/engines/opa_engine.py +493 -0
  37. proxilion/engines/simple.py +437 -0
  38. proxilion/exceptions.py +887 -0
  39. proxilion/guards/__init__.py +54 -0
  40. proxilion/guards/input_guard.py +522 -0
  41. proxilion/guards/output_guard.py +634 -0
  42. proxilion/observability/__init__.py +198 -0
  43. proxilion/observability/cost_tracker.py +866 -0
  44. proxilion/observability/hooks.py +683 -0
  45. proxilion/observability/metrics.py +798 -0
  46. proxilion/observability/session_cost_tracker.py +1063 -0
  47. proxilion/policies/__init__.py +67 -0
  48. proxilion/policies/base.py +304 -0
  49. proxilion/policies/builtin.py +486 -0
  50. proxilion/policies/registry.py +376 -0
  51. proxilion/providers/__init__.py +201 -0
  52. proxilion/providers/adapter.py +468 -0
  53. proxilion/providers/anthropic_adapter.py +330 -0
  54. proxilion/providers/gemini_adapter.py +391 -0
  55. proxilion/providers/openai_adapter.py +294 -0
  56. proxilion/py.typed +0 -0
  57. proxilion/resilience/__init__.py +81 -0
  58. proxilion/resilience/degradation.py +615 -0
  59. proxilion/resilience/fallback.py +555 -0
  60. proxilion/resilience/retry.py +554 -0
  61. proxilion/scheduling/__init__.py +57 -0
  62. proxilion/scheduling/priority_queue.py +419 -0
  63. proxilion/scheduling/scheduler.py +459 -0
  64. proxilion/security/__init__.py +244 -0
  65. proxilion/security/agent_trust.py +968 -0
  66. proxilion/security/behavioral_drift.py +794 -0
  67. proxilion/security/cascade_protection.py +869 -0
  68. proxilion/security/circuit_breaker.py +428 -0
  69. proxilion/security/cost_limiter.py +690 -0
  70. proxilion/security/idor_protection.py +460 -0
  71. proxilion/security/intent_capsule.py +849 -0
  72. proxilion/security/intent_validator.py +495 -0
  73. proxilion/security/memory_integrity.py +767 -0
  74. proxilion/security/rate_limiter.py +509 -0
  75. proxilion/security/scope_enforcer.py +680 -0
  76. proxilion/security/sequence_validator.py +636 -0
  77. proxilion/security/trust_boundaries.py +784 -0
  78. proxilion/streaming/__init__.py +70 -0
  79. proxilion/streaming/detector.py +761 -0
  80. proxilion/streaming/transformer.py +674 -0
  81. proxilion/timeouts/__init__.py +55 -0
  82. proxilion/timeouts/decorators.py +477 -0
  83. proxilion/timeouts/manager.py +545 -0
  84. proxilion/tools/__init__.py +69 -0
  85. proxilion/tools/decorators.py +493 -0
  86. proxilion/tools/registry.py +732 -0
  87. proxilion/types.py +339 -0
  88. proxilion/validation/__init__.py +93 -0
  89. proxilion/validation/pydantic_schema.py +351 -0
  90. proxilion/validation/schema.py +651 -0
  91. proxilion-0.0.1.dist-info/METADATA +872 -0
  92. proxilion-0.0.1.dist-info/RECORD +94 -0
  93. proxilion-0.0.1.dist-info/WHEEL +4 -0
  94. 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()