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,459 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Request scheduler with worker pool.
|
|
3
|
+
|
|
4
|
+
Provides concurrent execution of queued requests with
|
|
5
|
+
priority-based scheduling.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import threading
|
|
13
|
+
from collections.abc import Awaitable, Callable
|
|
14
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
from enum import Enum, auto
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from proxilion.scheduling.priority_queue import (
|
|
21
|
+
PriorityLevel,
|
|
22
|
+
PriorityQueue,
|
|
23
|
+
QueuedRequest,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SchedulerState(Enum):
|
|
30
|
+
"""Scheduler operational states."""
|
|
31
|
+
|
|
32
|
+
RUNNING = auto()
|
|
33
|
+
PAUSED = auto()
|
|
34
|
+
SHUTTING_DOWN = auto()
|
|
35
|
+
STOPPED = auto()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class SchedulerConfig:
|
|
40
|
+
"""
|
|
41
|
+
Configuration for the request scheduler.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
max_concurrent: Maximum concurrent request processing.
|
|
45
|
+
queue_size: Maximum queue size.
|
|
46
|
+
default_timeout: Default request timeout in seconds.
|
|
47
|
+
aging_interval: Queue aging interval in seconds.
|
|
48
|
+
aging_boost: Priority levels to boost on aging.
|
|
49
|
+
worker_idle_timeout: Seconds before idle workers check again.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> config = SchedulerConfig(
|
|
53
|
+
... max_concurrent=8,
|
|
54
|
+
... queue_size=500,
|
|
55
|
+
... default_timeout=60.0,
|
|
56
|
+
... )
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
max_concurrent: int = 4
|
|
60
|
+
queue_size: int = 1000
|
|
61
|
+
default_timeout: float | None = 60.0
|
|
62
|
+
aging_interval: float = 60.0
|
|
63
|
+
aging_boost: int = 1
|
|
64
|
+
worker_idle_timeout: float = 1.0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class SchedulerStats:
|
|
69
|
+
"""Statistics for the scheduler."""
|
|
70
|
+
|
|
71
|
+
requests_submitted: int = 0
|
|
72
|
+
requests_completed: int = 0
|
|
73
|
+
requests_failed: int = 0
|
|
74
|
+
requests_timed_out: int = 0
|
|
75
|
+
total_processing_time: float = 0.0
|
|
76
|
+
active_workers: int = 0
|
|
77
|
+
queue_size: int = 0
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class RequestScheduler:
|
|
81
|
+
"""
|
|
82
|
+
Request scheduler with worker pool and priority queue.
|
|
83
|
+
|
|
84
|
+
Manages concurrent execution of requests, respecting priority
|
|
85
|
+
levels and preventing starvation through aging.
|
|
86
|
+
|
|
87
|
+
Example:
|
|
88
|
+
>>> config = SchedulerConfig(max_concurrent=4)
|
|
89
|
+
>>> scheduler = RequestScheduler(config)
|
|
90
|
+
>>>
|
|
91
|
+
>>> # Define a handler
|
|
92
|
+
>>> def handle_request(payload):
|
|
93
|
+
... return f"Processed: {payload}"
|
|
94
|
+
>>>
|
|
95
|
+
>>> scheduler.set_handler(handle_request)
|
|
96
|
+
>>>
|
|
97
|
+
>>> # Submit requests
|
|
98
|
+
>>> request = QueuedRequest(
|
|
99
|
+
... priority=PriorityLevel.HIGH,
|
|
100
|
+
... payload={"task": "important"},
|
|
101
|
+
... )
|
|
102
|
+
>>> future = scheduler.submit(request)
|
|
103
|
+
>>> result = future.result()
|
|
104
|
+
>>>
|
|
105
|
+
>>> # Cleanup
|
|
106
|
+
>>> scheduler.shutdown()
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
config: SchedulerConfig | None = None,
|
|
112
|
+
handler: Callable[[Any], Any] | None = None,
|
|
113
|
+
async_handler: Callable[[Any], Awaitable[Any]] | None = None,
|
|
114
|
+
) -> None:
|
|
115
|
+
"""
|
|
116
|
+
Initialize the scheduler.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
config: Scheduler configuration.
|
|
120
|
+
handler: Synchronous request handler.
|
|
121
|
+
async_handler: Asynchronous request handler.
|
|
122
|
+
"""
|
|
123
|
+
self.config = config or SchedulerConfig()
|
|
124
|
+
self._handler = handler
|
|
125
|
+
self._async_handler = async_handler
|
|
126
|
+
|
|
127
|
+
self._queue = PriorityQueue(
|
|
128
|
+
max_size=self.config.queue_size,
|
|
129
|
+
aging_interval=self.config.aging_interval,
|
|
130
|
+
aging_boost=self.config.aging_boost,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
self._executor = ThreadPoolExecutor(
|
|
134
|
+
max_workers=self.config.max_concurrent,
|
|
135
|
+
thread_name_prefix="scheduler-worker",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self._state = SchedulerState.RUNNING
|
|
139
|
+
self._state_lock = threading.Lock()
|
|
140
|
+
self._stats = SchedulerStats()
|
|
141
|
+
self._stats_lock = threading.Lock()
|
|
142
|
+
|
|
143
|
+
# Track pending futures
|
|
144
|
+
self._pending_futures: dict[str, Future] = {}
|
|
145
|
+
self._futures_lock = threading.Lock()
|
|
146
|
+
|
|
147
|
+
# Start worker threads
|
|
148
|
+
self._workers: list[threading.Thread] = []
|
|
149
|
+
self._start_workers()
|
|
150
|
+
|
|
151
|
+
def _start_workers(self) -> None:
|
|
152
|
+
"""Start worker threads."""
|
|
153
|
+
for i in range(self.config.max_concurrent):
|
|
154
|
+
worker = threading.Thread(
|
|
155
|
+
target=self._worker_loop,
|
|
156
|
+
name=f"scheduler-worker-{i}",
|
|
157
|
+
daemon=True,
|
|
158
|
+
)
|
|
159
|
+
worker.start()
|
|
160
|
+
self._workers.append(worker)
|
|
161
|
+
|
|
162
|
+
def _worker_loop(self) -> None:
|
|
163
|
+
"""Worker loop that processes requests from the queue."""
|
|
164
|
+
while True:
|
|
165
|
+
with self._state_lock:
|
|
166
|
+
if self._state == SchedulerState.STOPPED:
|
|
167
|
+
return
|
|
168
|
+
if self._state == SchedulerState.SHUTTING_DOWN and self._queue.is_empty():
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
# Wait for state to allow processing
|
|
172
|
+
with self._state_lock:
|
|
173
|
+
while self._state == SchedulerState.PAUSED:
|
|
174
|
+
# Check periodically while paused
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
if self._state != SchedulerState.RUNNING:
|
|
178
|
+
if self._state == SchedulerState.STOPPED:
|
|
179
|
+
return
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
# Try to get a request
|
|
183
|
+
request = self._queue.dequeue(
|
|
184
|
+
block=True,
|
|
185
|
+
timeout=self.config.worker_idle_timeout,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if request is None:
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
# Process the request
|
|
192
|
+
self._process_request(request)
|
|
193
|
+
|
|
194
|
+
def _process_request(self, request: QueuedRequest) -> None:
|
|
195
|
+
"""Process a single request."""
|
|
196
|
+
start_time = datetime.now(timezone.utc)
|
|
197
|
+
result = None
|
|
198
|
+
error = None
|
|
199
|
+
|
|
200
|
+
with self._stats_lock:
|
|
201
|
+
self._stats.active_workers += 1
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
if self._handler:
|
|
205
|
+
result = self._handler(request.payload)
|
|
206
|
+
elif self._async_handler:
|
|
207
|
+
# Run async handler in event loop
|
|
208
|
+
loop = asyncio.new_event_loop()
|
|
209
|
+
try:
|
|
210
|
+
result = loop.run_until_complete(
|
|
211
|
+
self._async_handler(request.payload)
|
|
212
|
+
)
|
|
213
|
+
finally:
|
|
214
|
+
loop.close()
|
|
215
|
+
else:
|
|
216
|
+
# No handler, just return the payload
|
|
217
|
+
result = request.payload
|
|
218
|
+
|
|
219
|
+
# Call request callback if provided
|
|
220
|
+
if request.callback:
|
|
221
|
+
try:
|
|
222
|
+
request.callback(result)
|
|
223
|
+
except Exception as e:
|
|
224
|
+
logger.error(f"Request callback error: {e}")
|
|
225
|
+
|
|
226
|
+
with self._stats_lock:
|
|
227
|
+
self._stats.requests_completed += 1
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
error = e
|
|
231
|
+
logger.error(f"Request {request.id} failed: {e}")
|
|
232
|
+
with self._stats_lock:
|
|
233
|
+
self._stats.requests_failed += 1
|
|
234
|
+
|
|
235
|
+
finally:
|
|
236
|
+
# Update stats
|
|
237
|
+
processing_time = (
|
|
238
|
+
datetime.now(timezone.utc) - start_time
|
|
239
|
+
).total_seconds()
|
|
240
|
+
|
|
241
|
+
with self._stats_lock:
|
|
242
|
+
self._stats.active_workers -= 1
|
|
243
|
+
self._stats.total_processing_time += processing_time
|
|
244
|
+
|
|
245
|
+
# Complete the future
|
|
246
|
+
with self._futures_lock:
|
|
247
|
+
future = self._pending_futures.pop(request.id, None)
|
|
248
|
+
if future:
|
|
249
|
+
if error:
|
|
250
|
+
future.set_exception(error)
|
|
251
|
+
else:
|
|
252
|
+
future.set_result(result)
|
|
253
|
+
|
|
254
|
+
def set_handler(self, handler: Callable[[Any], Any]) -> None:
|
|
255
|
+
"""
|
|
256
|
+
Set the synchronous request handler.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
handler: Function to process request payloads.
|
|
260
|
+
"""
|
|
261
|
+
self._handler = handler
|
|
262
|
+
|
|
263
|
+
def set_async_handler(self, handler: Callable[[Any], Awaitable[Any]]) -> None:
|
|
264
|
+
"""
|
|
265
|
+
Set the asynchronous request handler.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
handler: Async function to process request payloads.
|
|
269
|
+
"""
|
|
270
|
+
self._async_handler = handler
|
|
271
|
+
|
|
272
|
+
def submit(
|
|
273
|
+
self,
|
|
274
|
+
request: QueuedRequest | None = None,
|
|
275
|
+
*,
|
|
276
|
+
priority: PriorityLevel = PriorityLevel.NORMAL,
|
|
277
|
+
payload: Any = None,
|
|
278
|
+
timeout: float | None = None,
|
|
279
|
+
callback: Callable[[Any], None] | None = None,
|
|
280
|
+
) -> Future:
|
|
281
|
+
"""
|
|
282
|
+
Submit a request for processing.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
request: Pre-built request object.
|
|
286
|
+
priority: Request priority (if building new request).
|
|
287
|
+
payload: Request payload (if building new request).
|
|
288
|
+
timeout: Request timeout (if building new request).
|
|
289
|
+
callback: Completion callback (if building new request).
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Future that will contain the result.
|
|
293
|
+
|
|
294
|
+
Raises:
|
|
295
|
+
RuntimeError: If scheduler is not running.
|
|
296
|
+
"""
|
|
297
|
+
with self._state_lock:
|
|
298
|
+
if self._state != SchedulerState.RUNNING:
|
|
299
|
+
raise RuntimeError(f"Scheduler is {self._state.name}")
|
|
300
|
+
|
|
301
|
+
# Build request if not provided
|
|
302
|
+
if request is None:
|
|
303
|
+
request = QueuedRequest(
|
|
304
|
+
priority=priority,
|
|
305
|
+
payload=payload,
|
|
306
|
+
timeout=timeout or self.config.default_timeout,
|
|
307
|
+
callback=callback,
|
|
308
|
+
)
|
|
309
|
+
elif timeout is None and self.config.default_timeout:
|
|
310
|
+
request.timeout = self.config.default_timeout
|
|
311
|
+
|
|
312
|
+
# Create future for result
|
|
313
|
+
future: Future = Future()
|
|
314
|
+
|
|
315
|
+
with self._futures_lock:
|
|
316
|
+
self._pending_futures[request.id] = future
|
|
317
|
+
|
|
318
|
+
# Enqueue the request
|
|
319
|
+
if not self._queue.enqueue(request, block=False):
|
|
320
|
+
with self._futures_lock:
|
|
321
|
+
self._pending_futures.pop(request.id, None)
|
|
322
|
+
future.set_exception(RuntimeError("Queue is full"))
|
|
323
|
+
return future
|
|
324
|
+
|
|
325
|
+
with self._stats_lock:
|
|
326
|
+
self._stats.requests_submitted += 1
|
|
327
|
+
self._stats.queue_size = self._queue.size()
|
|
328
|
+
|
|
329
|
+
return future
|
|
330
|
+
|
|
331
|
+
async def submit_async(
|
|
332
|
+
self,
|
|
333
|
+
request: QueuedRequest | None = None,
|
|
334
|
+
*,
|
|
335
|
+
priority: PriorityLevel = PriorityLevel.NORMAL,
|
|
336
|
+
payload: Any = None,
|
|
337
|
+
timeout: float | None = None,
|
|
338
|
+
) -> Any:
|
|
339
|
+
"""
|
|
340
|
+
Submit a request and await the result.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
request: Pre-built request object.
|
|
344
|
+
priority: Request priority (if building new request).
|
|
345
|
+
payload: Request payload (if building new request).
|
|
346
|
+
timeout: Request timeout (if building new request).
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
The processing result.
|
|
350
|
+
"""
|
|
351
|
+
future = self.submit(
|
|
352
|
+
request,
|
|
353
|
+
priority=priority,
|
|
354
|
+
payload=payload,
|
|
355
|
+
timeout=timeout,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Wait for future in async context
|
|
359
|
+
loop = asyncio.get_event_loop()
|
|
360
|
+
return await loop.run_in_executor(None, future.result)
|
|
361
|
+
|
|
362
|
+
def pause(self) -> None:
|
|
363
|
+
"""Pause request processing."""
|
|
364
|
+
with self._state_lock:
|
|
365
|
+
if self._state == SchedulerState.RUNNING:
|
|
366
|
+
self._state = SchedulerState.PAUSED
|
|
367
|
+
logger.info("Scheduler paused")
|
|
368
|
+
|
|
369
|
+
def resume(self) -> None:
|
|
370
|
+
"""Resume request processing."""
|
|
371
|
+
with self._state_lock:
|
|
372
|
+
if self._state == SchedulerState.PAUSED:
|
|
373
|
+
self._state = SchedulerState.RUNNING
|
|
374
|
+
logger.info("Scheduler resumed")
|
|
375
|
+
|
|
376
|
+
def shutdown(self, wait: bool = True, timeout: float | None = None) -> None:
|
|
377
|
+
"""
|
|
378
|
+
Shutdown the scheduler.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
wait: Whether to wait for pending requests.
|
|
382
|
+
timeout: Maximum time to wait for pending requests.
|
|
383
|
+
"""
|
|
384
|
+
with self._state_lock:
|
|
385
|
+
if self._state in (SchedulerState.SHUTTING_DOWN, SchedulerState.STOPPED):
|
|
386
|
+
return
|
|
387
|
+
self._state = SchedulerState.SHUTTING_DOWN
|
|
388
|
+
|
|
389
|
+
logger.info("Scheduler shutting down...")
|
|
390
|
+
|
|
391
|
+
if wait:
|
|
392
|
+
# Wait for workers to complete
|
|
393
|
+
for worker in self._workers:
|
|
394
|
+
worker.join(timeout=timeout)
|
|
395
|
+
|
|
396
|
+
# Force stop
|
|
397
|
+
with self._state_lock:
|
|
398
|
+
self._state = SchedulerState.STOPPED
|
|
399
|
+
|
|
400
|
+
# Cancel pending futures
|
|
401
|
+
with self._futures_lock:
|
|
402
|
+
for future in self._pending_futures.values():
|
|
403
|
+
if not future.done():
|
|
404
|
+
future.cancel()
|
|
405
|
+
self._pending_futures.clear()
|
|
406
|
+
|
|
407
|
+
self._executor.shutdown(wait=False)
|
|
408
|
+
logger.info("Scheduler stopped")
|
|
409
|
+
|
|
410
|
+
def get_queue_stats(self) -> dict[str, Any]:
|
|
411
|
+
"""
|
|
412
|
+
Get queue statistics.
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Dictionary with queue and scheduler statistics.
|
|
416
|
+
"""
|
|
417
|
+
queue_stats = self._queue.get_stats()
|
|
418
|
+
|
|
419
|
+
with self._stats_lock:
|
|
420
|
+
scheduler_stats = {
|
|
421
|
+
"requests_submitted": self._stats.requests_submitted,
|
|
422
|
+
"requests_completed": self._stats.requests_completed,
|
|
423
|
+
"requests_failed": self._stats.requests_failed,
|
|
424
|
+
"active_workers": self._stats.active_workers,
|
|
425
|
+
"total_processing_time": self._stats.total_processing_time,
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
"state": self._state.name,
|
|
430
|
+
"queue": queue_stats,
|
|
431
|
+
"scheduler": scheduler_stats,
|
|
432
|
+
"config": {
|
|
433
|
+
"max_concurrent": self.config.max_concurrent,
|
|
434
|
+
"queue_size": self.config.queue_size,
|
|
435
|
+
"default_timeout": self.config.default_timeout,
|
|
436
|
+
},
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
def get_pending_count(self) -> int:
|
|
440
|
+
"""Get number of pending requests."""
|
|
441
|
+
return self._queue.size()
|
|
442
|
+
|
|
443
|
+
def is_running(self) -> bool:
|
|
444
|
+
"""Check if scheduler is running."""
|
|
445
|
+
with self._state_lock:
|
|
446
|
+
return self._state == SchedulerState.RUNNING
|
|
447
|
+
|
|
448
|
+
def is_paused(self) -> bool:
|
|
449
|
+
"""Check if scheduler is paused."""
|
|
450
|
+
with self._state_lock:
|
|
451
|
+
return self._state == SchedulerState.PAUSED
|
|
452
|
+
|
|
453
|
+
def __enter__(self) -> RequestScheduler:
|
|
454
|
+
"""Context manager entry."""
|
|
455
|
+
return self
|
|
456
|
+
|
|
457
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
458
|
+
"""Context manager exit."""
|
|
459
|
+
self.shutdown(wait=True)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Security controls for Proxilion.
|
|
3
|
+
|
|
4
|
+
This module provides security mechanisms to protect against abuse
|
|
5
|
+
and ensure safe operation of LLM tool calls:
|
|
6
|
+
|
|
7
|
+
- Rate Limiting: Prevent unbounded consumption with token bucket,
|
|
8
|
+
sliding window, and multi-dimensional rate limiters.
|
|
9
|
+
|
|
10
|
+
- Circuit Breaker: Prevent cascading failures when tools or
|
|
11
|
+
external services fail repeatedly.
|
|
12
|
+
|
|
13
|
+
- IDOR Protection: Prevent Insecure Direct Object Reference attacks
|
|
14
|
+
by validating object IDs against user scopes.
|
|
15
|
+
|
|
16
|
+
- Intent Validation: Detect anomalous patterns in tool usage
|
|
17
|
+
without relying on LLM analysis.
|
|
18
|
+
|
|
19
|
+
- Cascade Protection: Prevent failures from propagating through
|
|
20
|
+
dependent tools and services.
|
|
21
|
+
|
|
22
|
+
- Trust Boundaries: Control inter-agent communication with trust
|
|
23
|
+
levels and delegation chains.
|
|
24
|
+
|
|
25
|
+
NEW - OWASP ASI Top 10 Protection:
|
|
26
|
+
|
|
27
|
+
- Memory Integrity (ASI06): Cryptographic context verification,
|
|
28
|
+
RAG poisoning detection.
|
|
29
|
+
|
|
30
|
+
- Agent Trust (ASI07): mTLS-style signed messaging between agents,
|
|
31
|
+
delegation chains with attestation.
|
|
32
|
+
|
|
33
|
+
- Intent Capsule (ASI01): Bind original user intent cryptographically
|
|
34
|
+
to prevent goal hijacking.
|
|
35
|
+
|
|
36
|
+
- Behavioral Drift (ASI10): Detect rogue agent behavior with
|
|
37
|
+
statistical baseline monitoring and kill switch.
|
|
38
|
+
|
|
39
|
+
Quick Start:
|
|
40
|
+
>>> from proxilion.security import (
|
|
41
|
+
... TokenBucketRateLimiter,
|
|
42
|
+
... CircuitBreaker,
|
|
43
|
+
... IDORProtector,
|
|
44
|
+
... IntentValidator,
|
|
45
|
+
... CascadeProtector,
|
|
46
|
+
... TrustEnforcer,
|
|
47
|
+
... )
|
|
48
|
+
>>>
|
|
49
|
+
>>> # Rate limiting
|
|
50
|
+
>>> rate_limiter = TokenBucketRateLimiter(capacity=100, refill_rate=10)
|
|
51
|
+
>>> if rate_limiter.allow_request("user_123"):
|
|
52
|
+
... # Process request
|
|
53
|
+
... pass
|
|
54
|
+
>>>
|
|
55
|
+
>>> # Circuit breaker
|
|
56
|
+
>>> breaker = CircuitBreaker(failure_threshold=5, reset_timeout=30)
|
|
57
|
+
>>> result = breaker.call(external_api_function)
|
|
58
|
+
>>>
|
|
59
|
+
>>> # IDOR protection
|
|
60
|
+
>>> protector = IDORProtector()
|
|
61
|
+
>>> protector.register_scope("user_123", "document", {"doc_1", "doc_2"})
|
|
62
|
+
>>> if protector.validate_access("user_123", "document", "doc_1"):
|
|
63
|
+
... # User can access this document
|
|
64
|
+
... pass
|
|
65
|
+
>>>
|
|
66
|
+
>>> # Cascade protection
|
|
67
|
+
>>> graph = DependencyGraph()
|
|
68
|
+
>>> graph.add_dependency("api", "database")
|
|
69
|
+
>>> cascade = CascadeProtector(graph)
|
|
70
|
+
>>> state = cascade.check_cascade_health("api")
|
|
71
|
+
>>>
|
|
72
|
+
>>> # Trust boundaries
|
|
73
|
+
>>> enforcer = TrustEnforcer()
|
|
74
|
+
>>> enforcer.register_agent(AgentIdentity(
|
|
75
|
+
... agent_id="main", trust_level=TrustLevel.INTERNAL
|
|
76
|
+
... ))
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
from proxilion.security.cascade_protection import (
|
|
80
|
+
CascadeAwareCircuitBreakerRegistry,
|
|
81
|
+
CascadeEvent,
|
|
82
|
+
CascadeProtector,
|
|
83
|
+
CascadeState,
|
|
84
|
+
DependencyGraph,
|
|
85
|
+
DependencyInfo,
|
|
86
|
+
)
|
|
87
|
+
from proxilion.security.circuit_breaker import (
|
|
88
|
+
CircuitBreaker,
|
|
89
|
+
CircuitBreakerRegistry,
|
|
90
|
+
CircuitState,
|
|
91
|
+
CircuitStats,
|
|
92
|
+
)
|
|
93
|
+
from proxilion.security.idor_protection import (
|
|
94
|
+
IDORProtector,
|
|
95
|
+
IDPattern,
|
|
96
|
+
ResourceScope,
|
|
97
|
+
)
|
|
98
|
+
from proxilion.security.intent_validator import (
|
|
99
|
+
AnomalyThresholds,
|
|
100
|
+
IntentValidator,
|
|
101
|
+
ValidationOutcome,
|
|
102
|
+
ValidationResult,
|
|
103
|
+
WorkflowState,
|
|
104
|
+
)
|
|
105
|
+
from proxilion.security.rate_limiter import (
|
|
106
|
+
MultiDimensionalRateLimiter,
|
|
107
|
+
RateLimitConfig,
|
|
108
|
+
RateLimiterMiddleware,
|
|
109
|
+
SlidingWindowRateLimiter,
|
|
110
|
+
TokenBucketRateLimiter,
|
|
111
|
+
)
|
|
112
|
+
from proxilion.security.sequence_validator import (
|
|
113
|
+
DEFAULT_SEQUENCE_RULES,
|
|
114
|
+
SequenceAction,
|
|
115
|
+
SequenceRule,
|
|
116
|
+
SequenceValidator,
|
|
117
|
+
SequenceViolation,
|
|
118
|
+
ToolCallRecord,
|
|
119
|
+
create_sequence_validator,
|
|
120
|
+
)
|
|
121
|
+
from proxilion.security.trust_boundaries import (
|
|
122
|
+
DEFAULT_BOUNDARIES,
|
|
123
|
+
AgentIdentity,
|
|
124
|
+
DelegationToken,
|
|
125
|
+
TrustBoundary,
|
|
126
|
+
TrustBoundaryViolation,
|
|
127
|
+
TrustEnforcer,
|
|
128
|
+
TrustLevel,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# New ASI Top 10 features
|
|
132
|
+
from proxilion.security.memory_integrity import (
|
|
133
|
+
ContextWindowGuard,
|
|
134
|
+
IntegrityViolation,
|
|
135
|
+
IntegrityViolationType,
|
|
136
|
+
MemoryIntegrityGuard,
|
|
137
|
+
RAGDocument,
|
|
138
|
+
RAGScanResult,
|
|
139
|
+
SignedMessage as MemorySignedMessage,
|
|
140
|
+
VerificationResult as MemoryVerificationResult,
|
|
141
|
+
)
|
|
142
|
+
from proxilion.security.agent_trust import (
|
|
143
|
+
AgentCredential,
|
|
144
|
+
AgentTrustManager,
|
|
145
|
+
DelegationChain,
|
|
146
|
+
DelegationToken as AgentDelegationToken,
|
|
147
|
+
SignedMessage as AgentSignedMessage,
|
|
148
|
+
TrustLevel as AgentTrustLevel,
|
|
149
|
+
VerificationResult as AgentVerificationResult,
|
|
150
|
+
)
|
|
151
|
+
from proxilion.security.intent_capsule import (
|
|
152
|
+
HijackDetection,
|
|
153
|
+
IntentCapsule,
|
|
154
|
+
IntentCapsuleManager,
|
|
155
|
+
IntentCategory,
|
|
156
|
+
IntentGuard,
|
|
157
|
+
IntentValidator as IntentHijackValidator,
|
|
158
|
+
)
|
|
159
|
+
from proxilion.security.behavioral_drift import (
|
|
160
|
+
BaselineStats,
|
|
161
|
+
BehavioralMonitor,
|
|
162
|
+
DriftDetector,
|
|
163
|
+
DriftMetric,
|
|
164
|
+
DriftResult,
|
|
165
|
+
KillSwitch,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
__all__ = [
|
|
169
|
+
# Rate limiting
|
|
170
|
+
"TokenBucketRateLimiter",
|
|
171
|
+
"SlidingWindowRateLimiter",
|
|
172
|
+
"MultiDimensionalRateLimiter",
|
|
173
|
+
"RateLimitConfig",
|
|
174
|
+
"RateLimiterMiddleware",
|
|
175
|
+
# Circuit breaker
|
|
176
|
+
"CircuitBreaker",
|
|
177
|
+
"CircuitBreakerRegistry",
|
|
178
|
+
"CircuitState",
|
|
179
|
+
"CircuitStats",
|
|
180
|
+
# IDOR protection
|
|
181
|
+
"IDORProtector",
|
|
182
|
+
"IDPattern",
|
|
183
|
+
"ResourceScope",
|
|
184
|
+
# Intent validation
|
|
185
|
+
"IntentValidator",
|
|
186
|
+
"ValidationOutcome",
|
|
187
|
+
"ValidationResult",
|
|
188
|
+
"AnomalyThresholds",
|
|
189
|
+
"WorkflowState",
|
|
190
|
+
# Cascade protection
|
|
191
|
+
"CascadeAwareCircuitBreakerRegistry",
|
|
192
|
+
"CascadeEvent",
|
|
193
|
+
"CascadeProtector",
|
|
194
|
+
"CascadeState",
|
|
195
|
+
"DependencyGraph",
|
|
196
|
+
"DependencyInfo",
|
|
197
|
+
# Trust boundaries
|
|
198
|
+
"AgentIdentity",
|
|
199
|
+
"DEFAULT_BOUNDARIES",
|
|
200
|
+
"DelegationToken",
|
|
201
|
+
"TrustBoundary",
|
|
202
|
+
"TrustBoundaryViolation",
|
|
203
|
+
"TrustEnforcer",
|
|
204
|
+
"TrustLevel",
|
|
205
|
+
# Sequence validation
|
|
206
|
+
"DEFAULT_SEQUENCE_RULES",
|
|
207
|
+
"SequenceAction",
|
|
208
|
+
"SequenceRule",
|
|
209
|
+
"SequenceValidator",
|
|
210
|
+
"SequenceViolation",
|
|
211
|
+
"ToolCallRecord",
|
|
212
|
+
"create_sequence_validator",
|
|
213
|
+
# Memory Integrity (ASI06)
|
|
214
|
+
"MemoryIntegrityGuard",
|
|
215
|
+
"ContextWindowGuard",
|
|
216
|
+
"IntegrityViolation",
|
|
217
|
+
"IntegrityViolationType",
|
|
218
|
+
"MemorySignedMessage",
|
|
219
|
+
"MemoryVerificationResult",
|
|
220
|
+
"RAGDocument",
|
|
221
|
+
"RAGScanResult",
|
|
222
|
+
# Agent Trust (ASI07)
|
|
223
|
+
"AgentTrustManager",
|
|
224
|
+
"AgentCredential",
|
|
225
|
+
"AgentDelegationToken",
|
|
226
|
+
"DelegationChain",
|
|
227
|
+
"AgentSignedMessage",
|
|
228
|
+
"AgentTrustLevel",
|
|
229
|
+
"AgentVerificationResult",
|
|
230
|
+
# Intent Capsule (ASI01)
|
|
231
|
+
"IntentCapsule",
|
|
232
|
+
"IntentGuard",
|
|
233
|
+
"IntentHijackValidator",
|
|
234
|
+
"IntentCapsuleManager",
|
|
235
|
+
"IntentCategory",
|
|
236
|
+
"HijackDetection",
|
|
237
|
+
# Behavioral Drift (ASI10)
|
|
238
|
+
"BehavioralMonitor",
|
|
239
|
+
"DriftDetector",
|
|
240
|
+
"KillSwitch",
|
|
241
|
+
"DriftResult",
|
|
242
|
+
"DriftMetric",
|
|
243
|
+
"BaselineStats",
|
|
244
|
+
]
|