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,554 @@
1
+ """
2
+ Retry logic with exponential backoff for AI operations.
3
+
4
+ Provides configurable retry policies with exponential backoff,
5
+ jitter, and customizable retry conditions.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import functools
12
+ import inspect
13
+ import logging
14
+ import random
15
+ import threading
16
+ import time
17
+ from collections.abc import Awaitable, Callable
18
+ from dataclasses import dataclass, field
19
+ from datetime import datetime, timezone
20
+ from typing import Any, ParamSpec, TypeVar
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ P = ParamSpec("P")
25
+ T = TypeVar("T")
26
+
27
+
28
+ @dataclass
29
+ class RetryPolicy:
30
+ """
31
+ Configuration for retry behavior.
32
+
33
+ Attributes:
34
+ max_attempts: Maximum number of attempts (including the first try).
35
+ base_delay: Base delay between retries in seconds.
36
+ max_delay: Maximum delay between retries in seconds.
37
+ exponential_base: Base for exponential backoff calculation.
38
+ jitter: Jitter factor as a fraction (0.1 = +/- 10%).
39
+ retryable_exceptions: Tuple of exception types that trigger retry.
40
+ retry_on: Optional custom function to determine if retry should occur.
41
+
42
+ Example:
43
+ >>> policy = RetryPolicy(
44
+ ... max_attempts=5,
45
+ ... base_delay=1.0,
46
+ ... max_delay=60.0,
47
+ ... exponential_base=2.0,
48
+ ... jitter=0.1,
49
+ ... )
50
+ """
51
+
52
+ max_attempts: int = 3
53
+ base_delay: float = 1.0
54
+ max_delay: float = 30.0
55
+ exponential_base: float = 2.0
56
+ jitter: float = 0.1
57
+ retryable_exceptions: tuple[type[Exception], ...] = (
58
+ TimeoutError,
59
+ ConnectionError,
60
+ OSError,
61
+ )
62
+ retry_on: Callable[[Exception], bool] | None = None
63
+
64
+ def __post_init__(self) -> None:
65
+ """Validate policy parameters."""
66
+ if self.max_attempts < 1:
67
+ raise ValueError("max_attempts must be at least 1")
68
+ if self.base_delay < 0:
69
+ raise ValueError("base_delay must be non-negative")
70
+ if self.max_delay < self.base_delay:
71
+ raise ValueError("max_delay must be >= base_delay")
72
+ if self.exponential_base < 1:
73
+ raise ValueError("exponential_base must be >= 1")
74
+ if not 0 <= self.jitter <= 1:
75
+ raise ValueError("jitter must be between 0 and 1")
76
+
77
+ def calculate_delay(self, attempt: int) -> float:
78
+ """
79
+ Calculate delay for a given attempt number.
80
+
81
+ Args:
82
+ attempt: The attempt number (1-indexed).
83
+
84
+ Returns:
85
+ Delay in seconds with jitter applied.
86
+ """
87
+ # Exponential backoff: base_delay * (exponential_base ^ (attempt - 1))
88
+ delay = self.base_delay * (self.exponential_base ** (attempt - 1))
89
+
90
+ # Cap at max_delay
91
+ delay = min(delay, self.max_delay)
92
+
93
+ # Apply jitter
94
+ if self.jitter > 0:
95
+ jitter_range = delay * self.jitter
96
+ delay = delay + random.uniform(-jitter_range, jitter_range)
97
+ delay = max(0, delay) # Ensure non-negative
98
+
99
+ return delay
100
+
101
+ def should_retry(self, exception: Exception, attempt: int) -> bool:
102
+ """
103
+ Determine if a retry should be attempted.
104
+
105
+ Args:
106
+ exception: The exception that occurred.
107
+ attempt: Current attempt number (1-indexed).
108
+
109
+ Returns:
110
+ True if retry should be attempted.
111
+ """
112
+ if attempt >= self.max_attempts:
113
+ return False
114
+
115
+ # Check custom retry condition
116
+ if self.retry_on is not None:
117
+ return self.retry_on(exception)
118
+
119
+ # Check if exception type is retryable
120
+ return isinstance(exception, self.retryable_exceptions)
121
+
122
+
123
+ @dataclass
124
+ class RetryContext:
125
+ """
126
+ Context information for a retry attempt.
127
+
128
+ Attributes:
129
+ attempt: Current attempt number (1-indexed).
130
+ total_delay: Total delay accumulated across all retries.
131
+ last_exception: The exception that triggered this retry.
132
+ should_retry: Whether another retry will be attempted.
133
+ started_at: When the retry sequence started.
134
+ policy: The retry policy being used.
135
+ """
136
+
137
+ attempt: int
138
+ total_delay: float
139
+ last_exception: Exception | None
140
+ should_retry: bool
141
+ started_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
142
+ policy: RetryPolicy | None = None
143
+
144
+ def elapsed(self) -> float:
145
+ """Get elapsed time since retry sequence started."""
146
+ return (datetime.now(timezone.utc) - self.started_at).total_seconds()
147
+
148
+
149
+ @dataclass
150
+ class RetryStats:
151
+ """
152
+ Statistics for retry operations.
153
+
154
+ Attributes:
155
+ total_attempts: Total number of attempts made.
156
+ successful_attempts: Number of successful completions.
157
+ failed_attempts: Number of failed attempts (excluding final failure).
158
+ total_delay: Total time spent in delays.
159
+ exceptions: List of exceptions encountered.
160
+ """
161
+
162
+ total_attempts: int = 0
163
+ successful_attempts: int = 0
164
+ failed_attempts: int = 0
165
+ total_delay: float = 0.0
166
+ exceptions: list[Exception] = field(default_factory=list)
167
+
168
+ def record_attempt(
169
+ self, success: bool, delay: float = 0.0, exception: Exception | None = None
170
+ ) -> None:
171
+ """Record an attempt."""
172
+ self.total_attempts += 1
173
+ self.total_delay += delay
174
+ if success:
175
+ self.successful_attempts += 1
176
+ else:
177
+ self.failed_attempts += 1
178
+ if exception:
179
+ self.exceptions.append(exception)
180
+
181
+ @property
182
+ def success_rate(self) -> float:
183
+ """Calculate success rate."""
184
+ if self.total_attempts == 0:
185
+ return 0.0
186
+ return self.successful_attempts / self.total_attempts
187
+
188
+ def to_dict(self) -> dict[str, Any]:
189
+ """Convert to dictionary."""
190
+ return {
191
+ "total_attempts": self.total_attempts,
192
+ "successful_attempts": self.successful_attempts,
193
+ "failed_attempts": self.failed_attempts,
194
+ "total_delay": self.total_delay,
195
+ "success_rate": self.success_rate,
196
+ "exception_types": [type(e).__name__ for e in self.exceptions],
197
+ }
198
+
199
+
200
+ # Default retry policy
201
+ DEFAULT_RETRY_POLICY = RetryPolicy(
202
+ max_attempts=3,
203
+ base_delay=1.0,
204
+ max_delay=30.0,
205
+ exponential_base=2.0,
206
+ jitter=0.1,
207
+ retryable_exceptions=(
208
+ TimeoutError,
209
+ ConnectionError,
210
+ OSError,
211
+ ),
212
+ )
213
+
214
+
215
+ def retry_with_backoff(
216
+ policy: RetryPolicy | None = None,
217
+ on_retry: Callable[[RetryContext], None] | None = None,
218
+ on_success: Callable[[T, RetryStats], None] | None = None,
219
+ on_failure: Callable[[Exception, RetryStats], None] | None = None,
220
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]:
221
+ """
222
+ Decorator that retries a function with exponential backoff.
223
+
224
+ Works with both synchronous and asynchronous functions.
225
+
226
+ Args:
227
+ policy: Retry policy to use. Defaults to DEFAULT_RETRY_POLICY.
228
+ on_retry: Callback invoked before each retry attempt.
229
+ on_success: Callback invoked on successful completion.
230
+ on_failure: Callback invoked on final failure.
231
+
232
+ Returns:
233
+ Decorated function with retry behavior.
234
+
235
+ Example:
236
+ >>> @retry_with_backoff(RetryPolicy(max_attempts=3))
237
+ ... def call_api():
238
+ ... return requests.get("https://api.example.com")
239
+
240
+ >>> @retry_with_backoff(
241
+ ... policy=RetryPolicy(max_attempts=5),
242
+ ... on_retry=lambda ctx: print(f"Retry {ctx.attempt}")
243
+ ... )
244
+ ... async def call_async_api():
245
+ ... return await client.fetch()
246
+ """
247
+ effective_policy = policy or DEFAULT_RETRY_POLICY
248
+
249
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
250
+ if inspect.iscoroutinefunction(func):
251
+
252
+ @functools.wraps(func)
253
+ async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
254
+ return await retry_async(
255
+ func,
256
+ *args,
257
+ policy=effective_policy,
258
+ on_retry=on_retry,
259
+ on_success=on_success,
260
+ on_failure=on_failure,
261
+ **kwargs,
262
+ )
263
+
264
+ return async_wrapper # type: ignore
265
+ else:
266
+
267
+ @functools.wraps(func)
268
+ def sync_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
269
+ return retry_sync(
270
+ func,
271
+ *args,
272
+ policy=effective_policy,
273
+ on_retry=on_retry,
274
+ on_success=on_success,
275
+ on_failure=on_failure,
276
+ **kwargs,
277
+ )
278
+
279
+ return sync_wrapper # type: ignore
280
+
281
+ return decorator
282
+
283
+
284
+ async def retry_async(
285
+ func: Callable[..., Awaitable[T]],
286
+ *args: Any,
287
+ policy: RetryPolicy | None = None,
288
+ on_retry: Callable[[RetryContext], None] | None = None,
289
+ on_success: Callable[[T, RetryStats], None] | None = None,
290
+ on_failure: Callable[[Exception, RetryStats], None] | None = None,
291
+ **kwargs: Any,
292
+ ) -> T:
293
+ """
294
+ Retry an async function with exponential backoff.
295
+
296
+ Args:
297
+ func: Async function to retry.
298
+ *args: Positional arguments for the function.
299
+ policy: Retry policy to use.
300
+ on_retry: Callback invoked before each retry.
301
+ on_success: Callback invoked on success.
302
+ on_failure: Callback invoked on final failure.
303
+ **kwargs: Keyword arguments for the function.
304
+
305
+ Returns:
306
+ Function result on success.
307
+
308
+ Raises:
309
+ Exception: The last exception if all retries fail.
310
+
311
+ Example:
312
+ >>> result = await retry_async(
313
+ ... fetch_data,
314
+ ... url="https://api.example.com",
315
+ ... policy=RetryPolicy(max_attempts=3),
316
+ ... )
317
+ """
318
+ effective_policy = policy or DEFAULT_RETRY_POLICY
319
+ stats = RetryStats()
320
+ started_at = datetime.now(timezone.utc)
321
+ last_exception: Exception | None = None
322
+ total_delay = 0.0
323
+
324
+ for attempt in range(1, effective_policy.max_attempts + 1):
325
+ try:
326
+ result = await func(*args, **kwargs)
327
+ stats.record_attempt(success=True)
328
+
329
+ if on_success:
330
+ on_success(result, stats)
331
+
332
+ logger.debug(
333
+ f"Retry succeeded on attempt {attempt}/{effective_policy.max_attempts}"
334
+ )
335
+ return result
336
+
337
+ except Exception as e:
338
+ last_exception = e
339
+ should_retry = effective_policy.should_retry(e, attempt)
340
+
341
+ stats.record_attempt(success=False, exception=e)
342
+
343
+ if should_retry:
344
+ delay = effective_policy.calculate_delay(attempt)
345
+ total_delay += delay
346
+
347
+ context = RetryContext(
348
+ attempt=attempt,
349
+ total_delay=total_delay,
350
+ last_exception=e,
351
+ should_retry=True,
352
+ started_at=started_at,
353
+ policy=effective_policy,
354
+ )
355
+
356
+ if on_retry:
357
+ on_retry(context)
358
+
359
+ logger.warning(
360
+ f"Attempt {attempt}/{effective_policy.max_attempts} failed: {e}. "
361
+ f"Retrying in {delay:.2f}s"
362
+ )
363
+
364
+ await asyncio.sleep(delay)
365
+ else:
366
+ break
367
+
368
+ # All retries exhausted
369
+ if on_failure and last_exception:
370
+ on_failure(last_exception, stats)
371
+
372
+ logger.error(
373
+ f"All {effective_policy.max_attempts} attempts failed. "
374
+ f"Last error: {last_exception}"
375
+ )
376
+
377
+ if last_exception:
378
+ raise last_exception
379
+ raise RuntimeError("Unexpected state: no exception but all retries failed")
380
+
381
+
382
+ def retry_sync(
383
+ func: Callable[..., T],
384
+ *args: Any,
385
+ policy: RetryPolicy | None = None,
386
+ on_retry: Callable[[RetryContext], None] | None = None,
387
+ on_success: Callable[[T, RetryStats], None] | None = None,
388
+ on_failure: Callable[[Exception, RetryStats], None] | None = None,
389
+ **kwargs: Any,
390
+ ) -> T:
391
+ """
392
+ Retry a sync function with exponential backoff.
393
+
394
+ Args:
395
+ func: Function to retry.
396
+ *args: Positional arguments for the function.
397
+ policy: Retry policy to use.
398
+ on_retry: Callback invoked before each retry.
399
+ on_success: Callback invoked on success.
400
+ on_failure: Callback invoked on final failure.
401
+ **kwargs: Keyword arguments for the function.
402
+
403
+ Returns:
404
+ Function result on success.
405
+
406
+ Raises:
407
+ Exception: The last exception if all retries fail.
408
+
409
+ Example:
410
+ >>> result = retry_sync(
411
+ ... requests.get,
412
+ ... "https://api.example.com",
413
+ ... policy=RetryPolicy(max_attempts=3),
414
+ ... )
415
+ """
416
+ effective_policy = policy or DEFAULT_RETRY_POLICY
417
+ stats = RetryStats()
418
+ started_at = datetime.now(timezone.utc)
419
+ last_exception: Exception | None = None
420
+ total_delay = 0.0
421
+
422
+ for attempt in range(1, effective_policy.max_attempts + 1):
423
+ try:
424
+ result = func(*args, **kwargs)
425
+ stats.record_attempt(success=True)
426
+
427
+ if on_success:
428
+ on_success(result, stats)
429
+
430
+ logger.debug(
431
+ f"Retry succeeded on attempt {attempt}/{effective_policy.max_attempts}"
432
+ )
433
+ return result
434
+
435
+ except Exception as e:
436
+ last_exception = e
437
+ should_retry = effective_policy.should_retry(e, attempt)
438
+
439
+ stats.record_attempt(success=False, exception=e)
440
+
441
+ if should_retry:
442
+ delay = effective_policy.calculate_delay(attempt)
443
+ total_delay += delay
444
+
445
+ context = RetryContext(
446
+ attempt=attempt,
447
+ total_delay=total_delay,
448
+ last_exception=e,
449
+ should_retry=True,
450
+ started_at=started_at,
451
+ policy=effective_policy,
452
+ )
453
+
454
+ if on_retry:
455
+ on_retry(context)
456
+
457
+ logger.warning(
458
+ f"Attempt {attempt}/{effective_policy.max_attempts} failed: {e}. "
459
+ f"Retrying in {delay:.2f}s"
460
+ )
461
+
462
+ time.sleep(delay)
463
+ else:
464
+ break
465
+
466
+ # All retries exhausted
467
+ if on_failure and last_exception:
468
+ on_failure(last_exception, stats)
469
+
470
+ logger.error(
471
+ f"All {effective_policy.max_attempts} attempts failed. "
472
+ f"Last error: {last_exception}"
473
+ )
474
+
475
+ if last_exception:
476
+ raise last_exception
477
+ raise RuntimeError("Unexpected state: no exception but all retries failed")
478
+
479
+
480
+ class RetryBudget:
481
+ """
482
+ A budget-based retry limiter to prevent retry storms.
483
+
484
+ Tracks retry attempts across multiple operations and limits
485
+ the total retry rate to prevent cascading failures.
486
+
487
+ Attributes:
488
+ max_retries_per_second: Maximum retries allowed per second.
489
+ window_seconds: Time window for tracking.
490
+
491
+ Example:
492
+ >>> budget = RetryBudget(max_retries_per_second=10)
493
+ >>> if budget.allow_retry():
494
+ ... # Proceed with retry
495
+ ... pass
496
+ """
497
+
498
+ def __init__(
499
+ self,
500
+ max_retries_per_second: float = 10.0,
501
+ window_seconds: float = 1.0,
502
+ ) -> None:
503
+ """
504
+ Initialize the retry budget.
505
+
506
+ Args:
507
+ max_retries_per_second: Maximum retries per second.
508
+ window_seconds: Time window for tracking.
509
+ """
510
+ self.max_retries_per_second = max_retries_per_second
511
+ self.window_seconds = window_seconds
512
+ self._tokens = max_retries_per_second
513
+ self._last_update = time.monotonic()
514
+ self._lock = threading.Lock()
515
+
516
+ def allow_retry(self) -> bool:
517
+ """
518
+ Check if a retry is allowed within budget.
519
+
520
+ Returns:
521
+ True if retry is allowed, False otherwise.
522
+ """
523
+ with self._lock:
524
+ now = time.monotonic()
525
+ elapsed = now - self._last_update
526
+ self._last_update = now
527
+
528
+ # Refill tokens
529
+ self._tokens = min(
530
+ self.max_retries_per_second,
531
+ self._tokens + elapsed * self.max_retries_per_second,
532
+ )
533
+
534
+ if self._tokens >= 1.0:
535
+ self._tokens -= 1.0
536
+ return True
537
+ return False
538
+
539
+ def reset(self) -> None:
540
+ """Reset the budget to full capacity."""
541
+ with self._lock:
542
+ self._tokens = self.max_retries_per_second
543
+ self._last_update = time.monotonic()
544
+
545
+ @property
546
+ def available_tokens(self) -> float:
547
+ """Get current available tokens."""
548
+ with self._lock:
549
+ now = time.monotonic()
550
+ elapsed = now - self._last_update
551
+ return min(
552
+ self.max_retries_per_second,
553
+ self._tokens + elapsed * self.max_retries_per_second,
554
+ )
@@ -0,0 +1,57 @@
1
+ """
2
+ Request scheduling and queuing for Proxilion.
3
+
4
+ This module provides priority-based request queuing to ensure
5
+ high-priority requests are processed first while preventing
6
+ starvation of lower-priority requests through aging.
7
+
8
+ Example:
9
+ >>> from proxilion.scheduling import (
10
+ ... PriorityLevel, QueuedRequest, PriorityQueue,
11
+ ... RequestScheduler, SchedulerConfig,
12
+ ... )
13
+ >>>
14
+ >>> # Create a priority queue
15
+ >>> queue = PriorityQueue(max_size=1000)
16
+ >>>
17
+ >>> # Enqueue requests with different priorities
18
+ >>> queue.enqueue(QueuedRequest(
19
+ ... id="req-1",
20
+ ... priority=PriorityLevel.HIGH,
21
+ ... payload={"task": "urgent"},
22
+ ... ))
23
+ >>> queue.enqueue(QueuedRequest(
24
+ ... id="req-2",
25
+ ... priority=PriorityLevel.LOW,
26
+ ... payload={"task": "background"},
27
+ ... ))
28
+ >>>
29
+ >>> # High priority dequeued first
30
+ >>> request = queue.dequeue()
31
+ >>> assert request.priority == PriorityLevel.HIGH
32
+ >>>
33
+ >>> # Use scheduler for concurrent processing
34
+ >>> config = SchedulerConfig(max_concurrent=4)
35
+ >>> scheduler = RequestScheduler(config)
36
+ >>> future = scheduler.submit(request)
37
+ """
38
+
39
+ from proxilion.scheduling.priority_queue import (
40
+ PriorityLevel,
41
+ PriorityQueue,
42
+ QueuedRequest,
43
+ )
44
+ from proxilion.scheduling.scheduler import (
45
+ RequestScheduler,
46
+ SchedulerConfig,
47
+ )
48
+
49
+ __all__ = [
50
+ # Priority queue
51
+ "PriorityLevel",
52
+ "QueuedRequest",
53
+ "PriorityQueue",
54
+ # Scheduler
55
+ "RequestScheduler",
56
+ "SchedulerConfig",
57
+ ]