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,545 @@
1
+ """
2
+ Timeout management for AI agent operations.
3
+
4
+ Provides configurable timeout handling with deadline tracking
5
+ for managing time budgets across multiple operations.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import contextvars
11
+ import threading
12
+ import time
13
+ from contextlib import contextmanager
14
+ from dataclasses import dataclass, field
15
+ from typing import Any
16
+
17
+
18
+ class TimeoutError(Exception):
19
+ """
20
+ Raised when an operation exceeds its timeout.
21
+
22
+ Attributes:
23
+ operation: Name of the operation that timed out.
24
+ timeout: The timeout value that was exceeded.
25
+ elapsed: Actual time elapsed before timeout.
26
+ message: Human-readable error message.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ message: str = "Operation timed out",
32
+ operation: str | None = None,
33
+ timeout: float | None = None,
34
+ elapsed: float | None = None,
35
+ ) -> None:
36
+ self.operation = operation
37
+ self.timeout = timeout
38
+ self.elapsed = elapsed
39
+ self.message = message
40
+ super().__init__(self._format_message())
41
+
42
+ def _format_message(self) -> str:
43
+ """Format the error message with details."""
44
+ parts = [self.message]
45
+ if self.operation:
46
+ parts.append(f"operation={self.operation}")
47
+ if self.timeout is not None:
48
+ parts.append(f"timeout={self.timeout:.2f}s")
49
+ if self.elapsed is not None:
50
+ parts.append(f"elapsed={self.elapsed:.2f}s")
51
+ return " ".join(parts)
52
+
53
+
54
+ @dataclass
55
+ class TimeoutConfig:
56
+ """
57
+ Configuration for timeout management.
58
+
59
+ Attributes:
60
+ default_timeout: Default timeout for operations in seconds.
61
+ tool_timeouts: Per-tool timeout overrides.
62
+ llm_timeout: Timeout for LLM API calls.
63
+ total_request_timeout: Total request budget timeout.
64
+ warn_at_percent: Percentage of timeout to trigger warning (0-100).
65
+
66
+ Example:
67
+ >>> config = TimeoutConfig(
68
+ ... default_timeout=30.0,
69
+ ... tool_timeouts={
70
+ ... "web_search": 60.0,
71
+ ... "database_query": 10.0,
72
+ ... "file_read": 5.0,
73
+ ... },
74
+ ... llm_timeout=120.0,
75
+ ... total_request_timeout=300.0,
76
+ ... )
77
+ """
78
+
79
+ default_timeout: float = 30.0
80
+ tool_timeouts: dict[str, float] = field(default_factory=dict)
81
+ llm_timeout: float = 120.0
82
+ total_request_timeout: float = 300.0
83
+ warn_at_percent: float = 80.0
84
+
85
+ def get_timeout(self, operation: str) -> float:
86
+ """
87
+ Get timeout for a specific operation.
88
+
89
+ Args:
90
+ operation: Name of the operation or tool.
91
+
92
+ Returns:
93
+ Timeout value in seconds.
94
+ """
95
+ return self.tool_timeouts.get(operation, self.default_timeout)
96
+
97
+ def set_tool_timeout(self, tool_name: str, timeout: float) -> None:
98
+ """
99
+ Set timeout for a specific tool.
100
+
101
+ Args:
102
+ tool_name: Name of the tool.
103
+ timeout: Timeout value in seconds.
104
+ """
105
+ self.tool_timeouts[tool_name] = timeout
106
+
107
+ def to_dict(self) -> dict[str, Any]:
108
+ """Convert config to dictionary."""
109
+ return {
110
+ "default_timeout": self.default_timeout,
111
+ "tool_timeouts": dict(self.tool_timeouts),
112
+ "llm_timeout": self.llm_timeout,
113
+ "total_request_timeout": self.total_request_timeout,
114
+ "warn_at_percent": self.warn_at_percent,
115
+ }
116
+
117
+ @classmethod
118
+ def from_dict(cls, data: dict[str, Any]) -> TimeoutConfig:
119
+ """Create config from dictionary."""
120
+ return cls(
121
+ default_timeout=data.get("default_timeout", 30.0),
122
+ tool_timeouts=data.get("tool_timeouts", {}),
123
+ llm_timeout=data.get("llm_timeout", 120.0),
124
+ total_request_timeout=data.get("total_request_timeout", 300.0),
125
+ warn_at_percent=data.get("warn_at_percent", 80.0),
126
+ )
127
+
128
+
129
+ # Context variable for current deadline
130
+ _current_deadline: contextvars.ContextVar[DeadlineContext | None] = contextvars.ContextVar(
131
+ "proxilion_deadline", default=None
132
+ )
133
+
134
+
135
+ def get_current_deadline() -> DeadlineContext | None:
136
+ """Get the current deadline context if any."""
137
+ return _current_deadline.get()
138
+
139
+
140
+ class DeadlineContext:
141
+ """
142
+ Context manager for tracking remaining time with a hard deadline.
143
+
144
+ Tracks the time budget for a request and provides methods to
145
+ check remaining time and whether the deadline has expired.
146
+ Supports nested deadlines (inner deadline cannot exceed outer).
147
+
148
+ Attributes:
149
+ timeout: Total timeout for this deadline context.
150
+ deadline: Monotonic timestamp when deadline expires.
151
+ started_at: Monotonic timestamp when context started.
152
+ operation: Optional name of the operation.
153
+
154
+ Example:
155
+ >>> async with DeadlineContext(timeout=30.0) as deadline:
156
+ ... result1 = await call_tool1(timeout=deadline.remaining())
157
+ ... result2 = await call_tool2(timeout=deadline.remaining())
158
+ ... # Automatically raises TimeoutError if deadline exceeded
159
+
160
+ >>> # With synchronous context manager
161
+ >>> with DeadlineContext(timeout=10.0) as deadline:
162
+ ... if deadline.remaining() > 5:
163
+ ... do_slow_operation()
164
+ """
165
+
166
+ def __init__(
167
+ self,
168
+ timeout: float,
169
+ operation: str | None = None,
170
+ raise_on_expire: bool = True,
171
+ ) -> None:
172
+ """
173
+ Initialize deadline context.
174
+
175
+ Args:
176
+ timeout: Timeout in seconds.
177
+ operation: Optional operation name for error messages.
178
+ raise_on_expire: Whether to raise TimeoutError when expired.
179
+ """
180
+ self.timeout = timeout
181
+ self.operation = operation
182
+ self.raise_on_expire = raise_on_expire
183
+ self._started_at: float | None = None
184
+ self._deadline: float | None = None
185
+ self._parent: DeadlineContext | None = None
186
+ self._token: contextvars.Token | None = None
187
+ self._lock = threading.Lock()
188
+
189
+ @property
190
+ def started_at(self) -> float:
191
+ """Get the start time (monotonic)."""
192
+ if self._started_at is None:
193
+ raise RuntimeError("DeadlineContext not started")
194
+ return self._started_at
195
+
196
+ @property
197
+ def deadline(self) -> float:
198
+ """Get the deadline (monotonic timestamp)."""
199
+ if self._deadline is None:
200
+ raise RuntimeError("DeadlineContext not started")
201
+ return self._deadline
202
+
203
+ def _start(self) -> None:
204
+ """Start the deadline tracking."""
205
+ now = time.monotonic()
206
+ self._started_at = now
207
+ self._deadline = now + self.timeout
208
+
209
+ # Check for parent deadline
210
+ self._parent = get_current_deadline()
211
+ if self._parent is not None:
212
+ # Inner deadline cannot exceed outer deadline
213
+ parent_deadline = self._parent.deadline
214
+ if self._deadline > parent_deadline:
215
+ self._deadline = parent_deadline
216
+
217
+ # Set as current deadline
218
+ self._token = _current_deadline.set(self)
219
+
220
+ def _stop(self) -> None:
221
+ """Stop the deadline tracking."""
222
+ if self._token is not None:
223
+ _current_deadline.reset(self._token)
224
+ self._token = None
225
+
226
+ def remaining(self) -> float:
227
+ """
228
+ Get remaining time until deadline.
229
+
230
+ Returns:
231
+ Remaining time in seconds.
232
+
233
+ Raises:
234
+ TimeoutError: If deadline has expired and raise_on_expire is True.
235
+ """
236
+ with self._lock:
237
+ if self._deadline is None:
238
+ raise RuntimeError("DeadlineContext not started")
239
+
240
+ remaining = self._deadline - time.monotonic()
241
+ if remaining <= 0:
242
+ if self.raise_on_expire:
243
+ raise TimeoutError(
244
+ message="Deadline exceeded",
245
+ operation=self.operation,
246
+ timeout=self.timeout,
247
+ elapsed=self.elapsed(),
248
+ )
249
+ return 0.0
250
+ return remaining
251
+
252
+ def remaining_or_default(self, default: float) -> float:
253
+ """
254
+ Get remaining time or default if expired.
255
+
256
+ Args:
257
+ default: Default value to return if deadline expired.
258
+
259
+ Returns:
260
+ Remaining time or default.
261
+ """
262
+ try:
263
+ return self.remaining()
264
+ except TimeoutError:
265
+ return default
266
+
267
+ def elapsed(self) -> float:
268
+ """
269
+ Get elapsed time since start.
270
+
271
+ Returns:
272
+ Elapsed time in seconds.
273
+ """
274
+ if self._started_at is None:
275
+ return 0.0
276
+ return time.monotonic() - self._started_at
277
+
278
+ def is_expired(self) -> bool:
279
+ """
280
+ Check if deadline has passed.
281
+
282
+ Returns:
283
+ True if deadline has expired.
284
+ """
285
+ if self._deadline is None:
286
+ return False
287
+ return time.monotonic() >= self._deadline
288
+
289
+ def check(self) -> None:
290
+ """
291
+ Check if deadline has expired and raise if so.
292
+
293
+ Raises:
294
+ TimeoutError: If deadline has expired.
295
+ """
296
+ if self.is_expired():
297
+ raise TimeoutError(
298
+ message="Deadline exceeded",
299
+ operation=self.operation,
300
+ timeout=self.timeout,
301
+ elapsed=self.elapsed(),
302
+ )
303
+
304
+ def get_timeout_for_operation(self, operation_timeout: float) -> float:
305
+ """
306
+ Get effective timeout for a sub-operation.
307
+
308
+ Returns the minimum of the operation's timeout and
309
+ the remaining deadline time.
310
+
311
+ Args:
312
+ operation_timeout: The desired timeout for the operation.
313
+
314
+ Returns:
315
+ Effective timeout (minimum of operation_timeout and remaining).
316
+ """
317
+ remaining = self.remaining()
318
+ return min(operation_timeout, remaining)
319
+
320
+ def __enter__(self) -> DeadlineContext:
321
+ """Enter context (synchronous)."""
322
+ self._start()
323
+ return self
324
+
325
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
326
+ """Exit context (synchronous)."""
327
+ self._stop()
328
+
329
+ async def __aenter__(self) -> DeadlineContext:
330
+ """Enter context (asynchronous)."""
331
+ self._start()
332
+ return self
333
+
334
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
335
+ """Exit context (asynchronous)."""
336
+ self._stop()
337
+
338
+
339
+ class TimeoutManager:
340
+ """
341
+ Central timeout configuration and management.
342
+
343
+ Manages timeout settings for different operations and provides
344
+ methods to create deadline contexts with appropriate timeouts.
345
+
346
+ Attributes:
347
+ config: The timeout configuration.
348
+
349
+ Example:
350
+ >>> manager = TimeoutManager(TimeoutConfig(
351
+ ... default_timeout=30.0,
352
+ ... tool_timeouts={"search": 60.0},
353
+ ... ))
354
+ >>> timeout = manager.get_timeout("search") # 60.0
355
+ >>> timeout = manager.get_timeout("unknown") # 30.0 (default)
356
+ """
357
+
358
+ def __init__(self, config: TimeoutConfig | None = None) -> None:
359
+ """
360
+ Initialize timeout manager.
361
+
362
+ Args:
363
+ config: Timeout configuration. Uses defaults if None.
364
+ """
365
+ self.config = config or TimeoutConfig()
366
+ self._lock = threading.RLock()
367
+
368
+ def get_timeout(self, operation: str) -> float:
369
+ """
370
+ Get timeout for a specific operation.
371
+
372
+ Args:
373
+ operation: Name of the operation or tool.
374
+
375
+ Returns:
376
+ Timeout value in seconds.
377
+ """
378
+ with self._lock:
379
+ return self.config.get_timeout(operation)
380
+
381
+ def get_llm_timeout(self) -> float:
382
+ """
383
+ Get timeout for LLM operations.
384
+
385
+ Returns:
386
+ LLM timeout in seconds.
387
+ """
388
+ return self.config.llm_timeout
389
+
390
+ def get_total_request_timeout(self) -> float:
391
+ """
392
+ Get total request budget timeout.
393
+
394
+ Returns:
395
+ Total request timeout in seconds.
396
+ """
397
+ return self.config.total_request_timeout
398
+
399
+ def set_tool_timeout(self, tool_name: str, timeout: float) -> None:
400
+ """
401
+ Set timeout for a specific tool.
402
+
403
+ Args:
404
+ tool_name: Name of the tool.
405
+ timeout: Timeout value in seconds.
406
+ """
407
+ with self._lock:
408
+ self.config.set_tool_timeout(tool_name, timeout)
409
+
410
+ def create_deadline(
411
+ self,
412
+ timeout: float | None = None,
413
+ operation: str | None = None,
414
+ ) -> DeadlineContext:
415
+ """
416
+ Create a deadline context with appropriate timeout.
417
+
418
+ Args:
419
+ timeout: Explicit timeout (uses total_request_timeout if None).
420
+ operation: Optional operation name.
421
+
422
+ Returns:
423
+ DeadlineContext for tracking the deadline.
424
+ """
425
+ effective_timeout = timeout or self.config.total_request_timeout
426
+ return DeadlineContext(timeout=effective_timeout, operation=operation)
427
+
428
+ def create_tool_deadline(self, tool_name: str) -> DeadlineContext:
429
+ """
430
+ Create a deadline context for a specific tool.
431
+
432
+ Args:
433
+ tool_name: Name of the tool.
434
+
435
+ Returns:
436
+ DeadlineContext with tool-specific timeout.
437
+ """
438
+ timeout = self.get_timeout(tool_name)
439
+ return DeadlineContext(timeout=timeout, operation=tool_name)
440
+
441
+ def create_llm_deadline(self) -> DeadlineContext:
442
+ """
443
+ Create a deadline context for LLM operations.
444
+
445
+ Returns:
446
+ DeadlineContext with LLM timeout.
447
+ """
448
+ return DeadlineContext(timeout=self.config.llm_timeout, operation="llm_call")
449
+
450
+ @contextmanager
451
+ def deadline_context(
452
+ self,
453
+ timeout: float | None = None,
454
+ operation: str | None = None,
455
+ ):
456
+ """
457
+ Context manager for deadline tracking.
458
+
459
+ Args:
460
+ timeout: Explicit timeout.
461
+ operation: Optional operation name.
462
+
463
+ Yields:
464
+ DeadlineContext instance.
465
+ """
466
+ ctx = self.create_deadline(timeout, operation)
467
+ with ctx:
468
+ yield ctx
469
+
470
+ def get_effective_timeout(
471
+ self,
472
+ operation: str,
473
+ requested_timeout: float | None = None,
474
+ ) -> float:
475
+ """
476
+ Get effective timeout considering current deadline.
477
+
478
+ If there's an active deadline context, returns the minimum
479
+ of the requested timeout and remaining deadline time.
480
+
481
+ Args:
482
+ operation: Name of the operation.
483
+ requested_timeout: Requested timeout (uses config if None).
484
+
485
+ Returns:
486
+ Effective timeout in seconds.
487
+ """
488
+ # Get configured timeout
489
+ config_timeout = self.get_timeout(operation)
490
+ timeout = requested_timeout if requested_timeout is not None else config_timeout
491
+
492
+ # Check for active deadline
493
+ current_deadline = get_current_deadline()
494
+ if current_deadline is not None:
495
+ try:
496
+ remaining = current_deadline.remaining()
497
+ return min(timeout, remaining)
498
+ except TimeoutError:
499
+ # Deadline already expired
500
+ return 0.0
501
+
502
+ return timeout
503
+
504
+ def to_dict(self) -> dict[str, Any]:
505
+ """Serialize manager configuration to dictionary."""
506
+ return self.config.to_dict()
507
+
508
+ @classmethod
509
+ def from_dict(cls, data: dict[str, Any]) -> TimeoutManager:
510
+ """Create manager from dictionary."""
511
+ config = TimeoutConfig.from_dict(data)
512
+ return cls(config)
513
+
514
+
515
+ # Default timeout manager instance
516
+ _default_manager: TimeoutManager | None = None
517
+ _manager_lock = threading.Lock()
518
+
519
+
520
+ def get_default_manager() -> TimeoutManager:
521
+ """
522
+ Get the default timeout manager.
523
+
524
+ Creates one with default settings if none exists.
525
+
526
+ Returns:
527
+ Default TimeoutManager instance.
528
+ """
529
+ global _default_manager
530
+ with _manager_lock:
531
+ if _default_manager is None:
532
+ _default_manager = TimeoutManager()
533
+ return _default_manager
534
+
535
+
536
+ def set_default_manager(manager: TimeoutManager) -> None:
537
+ """
538
+ Set the default timeout manager.
539
+
540
+ Args:
541
+ manager: TimeoutManager to use as default.
542
+ """
543
+ global _default_manager
544
+ with _manager_lock:
545
+ _default_manager = manager
@@ -0,0 +1,69 @@
1
+ """
2
+ Tool registry and management for AI agents.
3
+
4
+ Provides centralized management of tools with metadata, schemas,
5
+ and discovery capabilities. Makes it easy to manage tools across
6
+ an application and export them to different LLM provider formats.
7
+
8
+ Features:
9
+ - Centralized tool registration
10
+ - Schema inference from type hints
11
+ - Export to OpenAI, Anthropic, and Gemini formats
12
+ - Category and risk-level filtering
13
+ - Tool execution with authorization
14
+
15
+ Example:
16
+ >>> from proxilion.tools import (
17
+ ... ToolRegistry, ToolDefinition, ToolCategory, RiskLevel,
18
+ ... tool, get_global_registry,
19
+ ... )
20
+ >>>
21
+ >>> # Create a registry
22
+ >>> registry = ToolRegistry()
23
+ >>>
24
+ >>> # Register tools with decorator
25
+ >>> @tool(
26
+ ... name="search_web",
27
+ ... description="Search the web",
28
+ ... category=ToolCategory.SEARCH,
29
+ ... registry=registry,
30
+ ... )
31
+ ... def search_web(query: str, max_results: int = 10) -> list[dict]:
32
+ ... return perform_search(query, max_results)
33
+ >>>
34
+ >>> # Export to OpenAI format
35
+ >>> tools = registry.export_all(format="openai")
36
+ >>>
37
+ >>> # Execute a tool
38
+ >>> result = registry.execute("search_web", query="python async")
39
+ """
40
+
41
+ from proxilion.tools.decorators import (
42
+ infer_schema_from_function,
43
+ register_tool,
44
+ tool,
45
+ )
46
+ from proxilion.tools.registry import (
47
+ RiskLevel,
48
+ ToolCategory,
49
+ ToolDefinition,
50
+ ToolExecutionResult,
51
+ ToolRegistry,
52
+ get_global_registry,
53
+ set_global_registry,
54
+ )
55
+
56
+ __all__ = [
57
+ # Registry classes
58
+ "ToolCategory",
59
+ "RiskLevel",
60
+ "ToolDefinition",
61
+ "ToolRegistry",
62
+ "ToolExecutionResult",
63
+ "get_global_registry",
64
+ "set_global_registry",
65
+ # Decorators
66
+ "tool",
67
+ "register_tool",
68
+ "infer_schema_from_function",
69
+ ]