agent_os_kernel 3.1.0__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.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- nexus/tests/test_schemas.py +295 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Safe Calculator Tool.
|
|
5
|
+
|
|
6
|
+
Provides safe mathematical operations with:
|
|
7
|
+
- No eval() or exec() usage
|
|
8
|
+
- Expression parsing with allowed operations only
|
|
9
|
+
- Overflow protection
|
|
10
|
+
- Timeout for complex calculations
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import ast
|
|
14
|
+
import math
|
|
15
|
+
import operator
|
|
16
|
+
import re
|
|
17
|
+
from typing import Any, Dict, List, Optional, Union
|
|
18
|
+
|
|
19
|
+
from atr.decorator import tool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CalculatorTool:
|
|
23
|
+
"""
|
|
24
|
+
Safe calculator with expression evaluation.
|
|
25
|
+
|
|
26
|
+
Features:
|
|
27
|
+
- No eval()/exec() - uses safe expression parser
|
|
28
|
+
- Whitelisted operations only
|
|
29
|
+
- Overflow/underflow protection
|
|
30
|
+
- Precision control
|
|
31
|
+
- Common math functions (sqrt, sin, cos, log, etc.)
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
```python
|
|
35
|
+
calc = CalculatorTool(precision=10)
|
|
36
|
+
|
|
37
|
+
# Basic arithmetic
|
|
38
|
+
result = calc.evaluate("2 + 2 * 3") # 8
|
|
39
|
+
|
|
40
|
+
# With variables
|
|
41
|
+
result = calc.evaluate("x * 2 + y", {"x": 5, "y": 10}) # 20
|
|
42
|
+
|
|
43
|
+
# Math functions
|
|
44
|
+
result = calc.evaluate("sqrt(16) + sin(0)") # 4.0
|
|
45
|
+
```
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
# Allowed operators
|
|
49
|
+
OPERATORS = {
|
|
50
|
+
'+': operator.add,
|
|
51
|
+
'-': operator.sub,
|
|
52
|
+
'*': operator.mul,
|
|
53
|
+
'/': operator.truediv,
|
|
54
|
+
'//': operator.floordiv,
|
|
55
|
+
'%': operator.mod,
|
|
56
|
+
'**': operator.pow,
|
|
57
|
+
'^': operator.pow, # Alias for **
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Allowed functions
|
|
61
|
+
FUNCTIONS = {
|
|
62
|
+
'abs': abs,
|
|
63
|
+
'round': round,
|
|
64
|
+
'min': min,
|
|
65
|
+
'max': max,
|
|
66
|
+
'sum': sum,
|
|
67
|
+
# Math functions
|
|
68
|
+
'sqrt': math.sqrt,
|
|
69
|
+
'pow': math.pow,
|
|
70
|
+
'exp': math.exp,
|
|
71
|
+
'log': math.log,
|
|
72
|
+
'log10': math.log10,
|
|
73
|
+
'log2': math.log2,
|
|
74
|
+
'sin': math.sin,
|
|
75
|
+
'cos': math.cos,
|
|
76
|
+
'tan': math.tan,
|
|
77
|
+
'asin': math.asin,
|
|
78
|
+
'acos': math.acos,
|
|
79
|
+
'atan': math.atan,
|
|
80
|
+
'sinh': math.sinh,
|
|
81
|
+
'cosh': math.cosh,
|
|
82
|
+
'tanh': math.tanh,
|
|
83
|
+
'ceil': math.ceil,
|
|
84
|
+
'floor': math.floor,
|
|
85
|
+
'factorial': math.factorial,
|
|
86
|
+
'gcd': math.gcd,
|
|
87
|
+
'degrees': math.degrees,
|
|
88
|
+
'radians': math.radians,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Constants
|
|
92
|
+
CONSTANTS = {
|
|
93
|
+
'pi': math.pi,
|
|
94
|
+
'e': math.e,
|
|
95
|
+
'tau': math.tau,
|
|
96
|
+
'inf': math.inf,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
precision: int = 15,
|
|
102
|
+
max_value: float = 1e308,
|
|
103
|
+
allow_complex: bool = False
|
|
104
|
+
):
|
|
105
|
+
"""
|
|
106
|
+
Initialize calculator.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
precision: Decimal precision for results
|
|
110
|
+
max_value: Maximum allowed value
|
|
111
|
+
allow_complex: Whether to allow complex numbers
|
|
112
|
+
"""
|
|
113
|
+
self.precision = precision
|
|
114
|
+
self.max_value = max_value
|
|
115
|
+
self.allow_complex = allow_complex
|
|
116
|
+
|
|
117
|
+
# AST operator mapping for safe evaluation
|
|
118
|
+
_AST_OPS = {
|
|
119
|
+
ast.Add: operator.add,
|
|
120
|
+
ast.Sub: operator.sub,
|
|
121
|
+
ast.Mult: operator.mul,
|
|
122
|
+
ast.Div: operator.truediv,
|
|
123
|
+
ast.FloorDiv: operator.floordiv,
|
|
124
|
+
ast.Mod: operator.mod,
|
|
125
|
+
ast.Pow: operator.pow,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_AST_UNARY_OPS = {
|
|
129
|
+
ast.USub: operator.neg,
|
|
130
|
+
ast.UAdd: operator.pos,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
def _safe_eval_node(
|
|
134
|
+
self,
|
|
135
|
+
node: ast.AST,
|
|
136
|
+
namespace: Dict[str, Any],
|
|
137
|
+
) -> Any:
|
|
138
|
+
"""Recursively evaluate an AST node using only safe operations.
|
|
139
|
+
|
|
140
|
+
No eval()/compile() — walks the AST tree and computes results
|
|
141
|
+
using whitelisted operators and functions only.
|
|
142
|
+
"""
|
|
143
|
+
if isinstance(node, ast.Expression):
|
|
144
|
+
return self._safe_eval_node(node.body, namespace)
|
|
145
|
+
|
|
146
|
+
if isinstance(node, ast.Constant):
|
|
147
|
+
if isinstance(node.value, (int, float)):
|
|
148
|
+
return node.value
|
|
149
|
+
raise ValueError(f"Unsupported constant type: {type(node.value).__name__}")
|
|
150
|
+
|
|
151
|
+
if isinstance(node, ast.Name):
|
|
152
|
+
if node.id in namespace:
|
|
153
|
+
return namespace[node.id]
|
|
154
|
+
raise ValueError(f"Unknown variable: {node.id}")
|
|
155
|
+
|
|
156
|
+
if isinstance(node, ast.BinOp):
|
|
157
|
+
left = self._safe_eval_node(node.left, namespace)
|
|
158
|
+
right = self._safe_eval_node(node.right, namespace)
|
|
159
|
+
op_func = self._AST_OPS.get(type(node.op))
|
|
160
|
+
if op_func is None:
|
|
161
|
+
raise ValueError(f"Unsupported operator: {type(node.op).__name__}")
|
|
162
|
+
return op_func(left, right)
|
|
163
|
+
|
|
164
|
+
if isinstance(node, ast.UnaryOp):
|
|
165
|
+
operand = self._safe_eval_node(node.operand, namespace)
|
|
166
|
+
op_func = self._AST_UNARY_OPS.get(type(node.op))
|
|
167
|
+
if op_func is None:
|
|
168
|
+
raise ValueError(f"Unsupported unary operator: {type(node.op).__name__}")
|
|
169
|
+
return op_func(operand)
|
|
170
|
+
|
|
171
|
+
if isinstance(node, ast.Call):
|
|
172
|
+
if not isinstance(node.func, ast.Name):
|
|
173
|
+
raise ValueError("Only direct function calls are allowed (no attribute access)")
|
|
174
|
+
func_name = node.func.id
|
|
175
|
+
if func_name not in self.FUNCTIONS:
|
|
176
|
+
raise ValueError(f"Function not allowed: {func_name}")
|
|
177
|
+
func = self.FUNCTIONS[func_name]
|
|
178
|
+
args = [self._safe_eval_node(arg, namespace) for arg in node.args]
|
|
179
|
+
if node.keywords:
|
|
180
|
+
raise ValueError("Keyword arguments are not supported in function calls")
|
|
181
|
+
return func(*args)
|
|
182
|
+
|
|
183
|
+
if isinstance(node, ast.Tuple):
|
|
184
|
+
return tuple(self._safe_eval_node(elt, namespace) for elt in node.elts)
|
|
185
|
+
|
|
186
|
+
if isinstance(node, ast.List):
|
|
187
|
+
return [self._safe_eval_node(elt, namespace) for elt in node.elts]
|
|
188
|
+
|
|
189
|
+
raise ValueError(f"Unsupported expression type: {type(node).__name__}")
|
|
190
|
+
|
|
191
|
+
def _check_value(self, value: Union[int, float]) -> Union[int, float]:
|
|
192
|
+
"""Check value is within bounds."""
|
|
193
|
+
if isinstance(value, complex) and not self.allow_complex:
|
|
194
|
+
raise ValueError("Complex numbers not allowed")
|
|
195
|
+
|
|
196
|
+
if isinstance(value, (int, float)):
|
|
197
|
+
if abs(value) > self.max_value:
|
|
198
|
+
raise OverflowError(f"Value exceeds maximum: {self.max_value}")
|
|
199
|
+
|
|
200
|
+
return value
|
|
201
|
+
|
|
202
|
+
@tool(
|
|
203
|
+
name="calculate",
|
|
204
|
+
description="Evaluate a mathematical expression safely",
|
|
205
|
+
tags=["math", "calculate", "safe"]
|
|
206
|
+
)
|
|
207
|
+
def evaluate(
|
|
208
|
+
self,
|
|
209
|
+
expression: str,
|
|
210
|
+
variables: Optional[Dict[str, Union[int, float]]] = None
|
|
211
|
+
) -> Dict[str, Any]:
|
|
212
|
+
"""
|
|
213
|
+
Evaluate a mathematical expression.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
expression: Math expression (e.g., "2 + 2 * 3")
|
|
217
|
+
variables: Variable values (e.g., {"x": 5})
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Dict with result and metadata
|
|
221
|
+
"""
|
|
222
|
+
variables = variables or {}
|
|
223
|
+
|
|
224
|
+
# Validate expression length
|
|
225
|
+
if len(expression) > 1000:
|
|
226
|
+
return {
|
|
227
|
+
"success": False,
|
|
228
|
+
"error": "Expression too long (max 1000 chars)",
|
|
229
|
+
"result": None
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# Sanitize - only allow safe characters
|
|
233
|
+
allowed_chars = set("0123456789.+-*/^%() ,")
|
|
234
|
+
allowed_chars.update(set("abcdefghijklmnopqrstuvwxyz_"))
|
|
235
|
+
|
|
236
|
+
clean_expr = expression.lower()
|
|
237
|
+
for char in clean_expr:
|
|
238
|
+
if char not in allowed_chars:
|
|
239
|
+
return {
|
|
240
|
+
"success": False,
|
|
241
|
+
"error": f"Invalid character: '{char}'",
|
|
242
|
+
"result": None
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
# Build safe namespace
|
|
247
|
+
namespace = {}
|
|
248
|
+
namespace.update(self.CONSTANTS)
|
|
249
|
+
namespace.update(self.FUNCTIONS)
|
|
250
|
+
namespace.update(variables)
|
|
251
|
+
|
|
252
|
+
# Parse and evaluate using safe AST walker
|
|
253
|
+
|
|
254
|
+
# Replace ^ with ** for power
|
|
255
|
+
clean_expr = clean_expr.replace('^', '**')
|
|
256
|
+
|
|
257
|
+
# Parse expression
|
|
258
|
+
tree = ast.parse(clean_expr, mode='eval')
|
|
259
|
+
|
|
260
|
+
# Validate AST nodes
|
|
261
|
+
for node in ast.walk(tree):
|
|
262
|
+
if isinstance(node, ast.Call):
|
|
263
|
+
if isinstance(node.func, ast.Name):
|
|
264
|
+
if node.func.id not in self.FUNCTIONS:
|
|
265
|
+
return {
|
|
266
|
+
"success": False,
|
|
267
|
+
"error": f"Function not allowed: {node.func.id}",
|
|
268
|
+
"result": None
|
|
269
|
+
}
|
|
270
|
+
else:
|
|
271
|
+
return {
|
|
272
|
+
"success": False,
|
|
273
|
+
"error": "Only direct function calls are allowed (no attribute access)",
|
|
274
|
+
"result": None
|
|
275
|
+
}
|
|
276
|
+
elif isinstance(node, ast.Name):
|
|
277
|
+
if node.id not in namespace:
|
|
278
|
+
return {
|
|
279
|
+
"success": False,
|
|
280
|
+
"error": f"Unknown variable: {node.id}",
|
|
281
|
+
"result": None
|
|
282
|
+
}
|
|
283
|
+
elif isinstance(node, ast.Attribute):
|
|
284
|
+
return {
|
|
285
|
+
"success": False,
|
|
286
|
+
"error": "Attribute access is not allowed",
|
|
287
|
+
"result": None
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# Evaluate using safe AST walker (no eval/compile)
|
|
291
|
+
result = self._safe_eval_node(tree.body, namespace)
|
|
292
|
+
|
|
293
|
+
# Check result
|
|
294
|
+
result = self._check_value(result)
|
|
295
|
+
|
|
296
|
+
# Round if float
|
|
297
|
+
if isinstance(result, float):
|
|
298
|
+
result = round(result, self.precision)
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
"success": True,
|
|
302
|
+
"result": result,
|
|
303
|
+
"expression": expression,
|
|
304
|
+
"type": type(result).__name__
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
except SyntaxError as e:
|
|
308
|
+
return {
|
|
309
|
+
"success": False,
|
|
310
|
+
"error": f"Syntax error: {e}",
|
|
311
|
+
"result": None
|
|
312
|
+
}
|
|
313
|
+
except (ValueError, TypeError, OverflowError, ZeroDivisionError) as e:
|
|
314
|
+
return {
|
|
315
|
+
"success": False,
|
|
316
|
+
"error": str(e),
|
|
317
|
+
"result": None
|
|
318
|
+
}
|
|
319
|
+
except Exception as e:
|
|
320
|
+
return {
|
|
321
|
+
"success": False,
|
|
322
|
+
"error": f"Calculation error: {e}",
|
|
323
|
+
"result": None
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@tool(
|
|
327
|
+
name="add",
|
|
328
|
+
description="Add two or more numbers",
|
|
329
|
+
tags=["math", "arithmetic", "safe"]
|
|
330
|
+
)
|
|
331
|
+
def add(self, *numbers: Union[int, float]) -> Dict[str, Any]:
|
|
332
|
+
"""Add numbers."""
|
|
333
|
+
try:
|
|
334
|
+
result = sum(numbers)
|
|
335
|
+
return {"success": True, "result": self._check_value(result)}
|
|
336
|
+
except Exception as e:
|
|
337
|
+
return {"success": False, "error": str(e), "result": None}
|
|
338
|
+
|
|
339
|
+
@tool(
|
|
340
|
+
name="subtract",
|
|
341
|
+
description="Subtract numbers (a - b - c - ...)",
|
|
342
|
+
tags=["math", "arithmetic", "safe"]
|
|
343
|
+
)
|
|
344
|
+
def subtract(self, *numbers: Union[int, float]) -> Dict[str, Any]:
|
|
345
|
+
"""Subtract numbers."""
|
|
346
|
+
try:
|
|
347
|
+
if not numbers:
|
|
348
|
+
return {"success": False, "error": "No numbers provided", "result": None}
|
|
349
|
+
result = numbers[0]
|
|
350
|
+
for n in numbers[1:]:
|
|
351
|
+
result -= n
|
|
352
|
+
return {"success": True, "result": self._check_value(result)}
|
|
353
|
+
except Exception as e:
|
|
354
|
+
return {"success": False, "error": str(e), "result": None}
|
|
355
|
+
|
|
356
|
+
@tool(
|
|
357
|
+
name="multiply",
|
|
358
|
+
description="Multiply numbers",
|
|
359
|
+
tags=["math", "arithmetic", "safe"]
|
|
360
|
+
)
|
|
361
|
+
def multiply(self, *numbers: Union[int, float]) -> Dict[str, Any]:
|
|
362
|
+
"""Multiply numbers."""
|
|
363
|
+
try:
|
|
364
|
+
result = 1
|
|
365
|
+
for n in numbers:
|
|
366
|
+
result *= n
|
|
367
|
+
return {"success": True, "result": self._check_value(result)}
|
|
368
|
+
except Exception as e:
|
|
369
|
+
return {"success": False, "error": str(e), "result": None}
|
|
370
|
+
|
|
371
|
+
@tool(
|
|
372
|
+
name="divide",
|
|
373
|
+
description="Divide numbers (a / b)",
|
|
374
|
+
tags=["math", "arithmetic", "safe"]
|
|
375
|
+
)
|
|
376
|
+
def divide(self, a: Union[int, float], b: Union[int, float]) -> Dict[str, Any]:
|
|
377
|
+
"""Divide a by b."""
|
|
378
|
+
try:
|
|
379
|
+
if b == 0:
|
|
380
|
+
return {"success": False, "error": "Division by zero", "result": None}
|
|
381
|
+
result = a / b
|
|
382
|
+
return {"success": True, "result": round(self._check_value(result), self.precision)}
|
|
383
|
+
except Exception as e:
|
|
384
|
+
return {"success": False, "error": str(e), "result": None}
|
|
385
|
+
|
|
386
|
+
@tool(
|
|
387
|
+
name="power",
|
|
388
|
+
description="Calculate a raised to power b (a^b)",
|
|
389
|
+
tags=["math", "arithmetic", "safe"]
|
|
390
|
+
)
|
|
391
|
+
def power(self, base: Union[int, float], exponent: Union[int, float]) -> Dict[str, Any]:
|
|
392
|
+
"""Calculate power."""
|
|
393
|
+
try:
|
|
394
|
+
# Limit exponent to prevent huge calculations
|
|
395
|
+
if abs(exponent) > 1000:
|
|
396
|
+
return {"success": False, "error": "Exponent too large", "result": None}
|
|
397
|
+
result = base ** exponent
|
|
398
|
+
return {"success": True, "result": self._check_value(result)}
|
|
399
|
+
except Exception as e:
|
|
400
|
+
return {"success": False, "error": str(e), "result": None}
|
|
401
|
+
|
|
402
|
+
@tool(
|
|
403
|
+
name="sqrt",
|
|
404
|
+
description="Calculate square root",
|
|
405
|
+
tags=["math", "safe"]
|
|
406
|
+
)
|
|
407
|
+
def sqrt(self, n: Union[int, float]) -> Dict[str, Any]:
|
|
408
|
+
"""Calculate square root."""
|
|
409
|
+
try:
|
|
410
|
+
if n < 0 and not self.allow_complex:
|
|
411
|
+
return {"success": False, "error": "Cannot take sqrt of negative number", "result": None}
|
|
412
|
+
result = math.sqrt(n)
|
|
413
|
+
return {"success": True, "result": round(result, self.precision)}
|
|
414
|
+
except Exception as e:
|
|
415
|
+
return {"success": False, "error": str(e), "result": None}
|
|
416
|
+
|
|
417
|
+
@tool(
|
|
418
|
+
name="percentage",
|
|
419
|
+
description="Calculate percentage (what is x% of y)",
|
|
420
|
+
tags=["math", "percentage", "safe"]
|
|
421
|
+
)
|
|
422
|
+
def percentage(self, percent: Union[int, float], of_value: Union[int, float]) -> Dict[str, Any]:
|
|
423
|
+
"""Calculate percentage."""
|
|
424
|
+
try:
|
|
425
|
+
result = (percent / 100) * of_value
|
|
426
|
+
return {
|
|
427
|
+
"success": True,
|
|
428
|
+
"result": round(result, self.precision),
|
|
429
|
+
"description": f"{percent}% of {of_value} = {round(result, self.precision)}"
|
|
430
|
+
}
|
|
431
|
+
except Exception as e:
|
|
432
|
+
return {"success": False, "error": str(e), "result": None}
|
|
433
|
+
|
|
434
|
+
@tool(
|
|
435
|
+
name="statistics",
|
|
436
|
+
description="Calculate basic statistics (mean, median, std dev)",
|
|
437
|
+
tags=["math", "statistics", "safe"]
|
|
438
|
+
)
|
|
439
|
+
def statistics(self, numbers: List[Union[int, float]]) -> Dict[str, Any]:
|
|
440
|
+
"""Calculate basic statistics."""
|
|
441
|
+
try:
|
|
442
|
+
if not numbers:
|
|
443
|
+
return {"success": False, "error": "No numbers provided", "result": None}
|
|
444
|
+
|
|
445
|
+
import statistics as stats
|
|
446
|
+
|
|
447
|
+
n = len(numbers)
|
|
448
|
+
mean = stats.mean(numbers)
|
|
449
|
+
median = stats.median(numbers)
|
|
450
|
+
|
|
451
|
+
result = {
|
|
452
|
+
"count": n,
|
|
453
|
+
"sum": sum(numbers),
|
|
454
|
+
"mean": round(mean, self.precision),
|
|
455
|
+
"median": round(median, self.precision),
|
|
456
|
+
"min": min(numbers),
|
|
457
|
+
"max": max(numbers),
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if n > 1:
|
|
461
|
+
result["std_dev"] = round(stats.stdev(numbers), self.precision)
|
|
462
|
+
result["variance"] = round(stats.variance(numbers), self.precision)
|
|
463
|
+
|
|
464
|
+
return {"success": True, "result": result}
|
|
465
|
+
|
|
466
|
+
except Exception as e:
|
|
467
|
+
return {"success": False, "error": str(e), "result": None}
|