Initial release v0.1.0 - 210 tools across 42 domains
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- LICENSE +21 -0
- README.md +251 -0
- fluxem_tools/__init__.py +186 -0
- fluxem_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- fluxem_tools/__pycache__/registry.cpython-314.pyc +0 -0
- fluxem_tools/domains/__init__.py +156 -0
- fluxem_tools/domains/__pycache__/__init__.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/acoustics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/arithmetic.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/astronomy.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/biology.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/calculus.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/chemistry.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/color.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/combinatorics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/control_systems.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/cooking.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/currency.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/data.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/diy.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/electrical.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/finance.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/fitness.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/fluid_dynamics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/gardening.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/geometric_algebra.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/geometry.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/geospatial.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/graphs.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/information_theory.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/logic.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/math_advanced.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/music.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/nuclear.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/number_theory.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/optics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/optimization.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/pharmacology.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/photography.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/physics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/probability.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/security.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/sets.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/signal_processing.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/statistics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/temporal.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/text.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/thermodynamics.cpython-314.pyc +0 -0
- fluxem_tools/domains/__pycache__/travel.cpython-314.pyc +0 -0
- fluxem_tools/domains/acoustics.py +379 -0
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Hunter Bown
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FluxEM Tools
|
| 2 |
+
|
| 3 |
+
**210+ deterministic computation tools for LLM tool-calling.**
|
| 4 |
+
|
| 5 |
+
This is a **tool package**, not a fine-tuned model. Use with any capable LLM (GPT-4, Claude, Qwen, Llama, Gemini, etc.) that supports function/tool calling.
|
| 6 |
+
|
| 7 |
+
## Installation
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install fluxem-tools
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
## Quick Start
|
| 14 |
+
|
| 15 |
+
```python
|
| 16 |
+
from fluxem_tools import get_registry, call_tool
|
| 17 |
+
|
| 18 |
+
# Get the tool registry
|
| 19 |
+
registry = get_registry()
|
| 20 |
+
print(f"Total tools: {len(registry)}") # 210+
|
| 21 |
+
|
| 22 |
+
# Call a tool directly
|
| 23 |
+
result = call_tool("arithmetic", "2 + 3 * 4")
|
| 24 |
+
print(result) # 14
|
| 25 |
+
|
| 26 |
+
# Physics calculation
|
| 27 |
+
ohms = call_tool("electrical_ohms_law", {"voltage": 12, "current": 2})
|
| 28 |
+
print(f"Resistance: {ohms} ohms") # 6.0
|
| 29 |
+
|
| 30 |
+
# List available domains
|
| 31 |
+
from fluxem_tools import list_domains
|
| 32 |
+
print(list_domains()) # ['arithmetic', 'physics', 'chemistry', ...]
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
## LLM Integration
|
| 36 |
+
|
| 37 |
+
### OpenAI
|
| 38 |
+
|
| 39 |
+
```python
|
| 40 |
+
from openai import OpenAI
|
| 41 |
+
from fluxem_tools import get_registry
|
| 42 |
+
|
| 43 |
+
client = OpenAI()
|
| 44 |
+
tools = get_registry().to_openai_tools()
|
| 45 |
+
|
| 46 |
+
response = client.chat.completions.create(
|
| 47 |
+
model="gpt-4",
|
| 48 |
+
messages=[{"role": "user", "content": "What is 23 * 47?"}],
|
| 49 |
+
tools=tools
|
| 50 |
+
)
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
### Anthropic Claude
|
| 54 |
+
|
| 55 |
+
```python
|
| 56 |
+
import anthropic
|
| 57 |
+
from fluxem_tools import get_registry
|
| 58 |
+
|
| 59 |
+
client = anthropic.Anthropic()
|
| 60 |
+
tools = get_registry().to_anthropic_tools()
|
| 61 |
+
|
| 62 |
+
response = client.messages.create(
|
| 63 |
+
model="claude-3-opus-20240229",
|
| 64 |
+
messages=[{"role": "user", "content": "Calculate BMI for 70kg, 1.75m"}],
|
| 65 |
+
tools=tools
|
| 66 |
+
)
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
### HuggingFace Transformers
|
| 70 |
+
|
| 71 |
+
```python
|
| 72 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 73 |
+
from fluxem_tools import get_registry
|
| 74 |
+
|
| 75 |
+
model_id = "Qwen/Qwen3-4B-Instruct"
|
| 76 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
| 77 |
+
model = AutoModelForCausalLM.from_pretrained(model_id)
|
| 78 |
+
|
| 79 |
+
tools = get_registry().to_openai_tools()
|
| 80 |
+
# Use with model's tool calling capabilities
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
## Tool Categories (40+ domains)
|
| 84 |
+
|
| 85 |
+
### Core Mathematics (30 tools)
|
| 86 |
+
- **arithmetic**: Basic operations, expressions
|
| 87 |
+
- **number_theory**: Primes, GCD, LCM, factorization
|
| 88 |
+
- **combinatorics**: Factorial, permutations, combinations
|
| 89 |
+
- **statistics**: Mean, median, variance, correlation
|
| 90 |
+
- **probability**: Distributions, Bayes' rule
|
| 91 |
+
- **calculus**: Derivatives, integrals
|
| 92 |
+
|
| 93 |
+
### Science & Engineering (60+ tools)
|
| 94 |
+
- **physics**: Unit conversion, dimensional analysis
|
| 95 |
+
- **chemistry**: Molecular weight, balancing equations
|
| 96 |
+
- **biology**: DNA/RNA analysis, protein calculations
|
| 97 |
+
- **electrical**: Ohm's law, circuits, power
|
| 98 |
+
- **thermodynamics**: Heat transfer, gas laws, Carnot efficiency
|
| 99 |
+
- **acoustics**: Decibels, Doppler effect, wavelength
|
| 100 |
+
- **astronomy**: Orbital mechanics, parallax, moon phase
|
| 101 |
+
- **optics**: Lenses, refraction, diffraction
|
| 102 |
+
- **fluid_dynamics**: Reynolds number, Bernoulli, drag
|
| 103 |
+
- **nuclear**: Radioactive decay, binding energy
|
| 104 |
+
|
| 105 |
+
### Advanced Mathematics (25 tools)
|
| 106 |
+
- **math_advanced**: Vectors, matrices, complex numbers
|
| 107 |
+
- **geometry**: Distance, rotation, transformations
|
| 108 |
+
- **graphs**: Shortest path, connectivity
|
| 109 |
+
- **sets**: Union, intersection, complement
|
| 110 |
+
- **logic**: Tautology checking
|
| 111 |
+
- **geometric_algebra**: Clifford algebra Cl(3,0)
|
| 112 |
+
|
| 113 |
+
### Data & Information (20 tools)
|
| 114 |
+
- **data**: Array operations, records
|
| 115 |
+
- **information_theory**: Entropy, KL divergence
|
| 116 |
+
- **signal_processing**: Convolution, DFT, filters
|
| 117 |
+
- **text**: Levenshtein distance, readability metrics
|
| 118 |
+
|
| 119 |
+
### Finance & Economics (15 tools)
|
| 120 |
+
- **finance**: Compound interest, NPV, loan payments
|
| 121 |
+
- **currency**: Exchange rates, inflation adjustment
|
| 122 |
+
|
| 123 |
+
### Everyday Practical (50+ tools)
|
| 124 |
+
- **cooking**: Recipe scaling, unit conversion
|
| 125 |
+
- **fitness**: BMI, BMR, heart rate zones
|
| 126 |
+
- **travel**: Timezone conversion, fuel consumption
|
| 127 |
+
- **diy**: Paint area, tile count, lumber calculation
|
| 128 |
+
- **photography**: Exposure, depth of field, focal length
|
| 129 |
+
- **gardening**: Soil volume, water needs, spacing
|
| 130 |
+
- **security**: RBAC permission checking
|
| 131 |
+
|
| 132 |
+
### Music & Time (10 tools)
|
| 133 |
+
- **music**: Chord analysis, transposition
|
| 134 |
+
- **temporal**: Date arithmetic, day of week
|
| 135 |
+
|
| 136 |
+
## Tool Reference
|
| 137 |
+
|
| 138 |
+
Every tool is deterministic - same input always produces same output.
|
| 139 |
+
|
| 140 |
+
### Example Tools
|
| 141 |
+
|
| 142 |
+
| Tool | Description | Example |
|
| 143 |
+
|------|-------------|---------|
|
| 144 |
+
| `arithmetic` | Evaluate math expression | `"2 + 3 * 4"` → `14` |
|
| 145 |
+
| `electrical_ohms_law` | V = I × R | `{V:12, I:2}` → `6.0` |
|
| 146 |
+
| `chemistry_mw` | Molecular weight | `"H2O"` → `18.015` |
|
| 147 |
+
| `fitness_bmi` | Body Mass Index | `{weight:70, height:1.75}` → `22.86` |
|
| 148 |
+
| `geo_distance` | Haversine distance | NYC to LA → `3935746 m` |
|
| 149 |
+
| `acoustics_db_add` | Add decibels | `{60, 60}` → `63.01` |
|
| 150 |
+
| `photo_depth_of_field` | DoF calculation | Near/far limits |
|
| 151 |
+
|
| 152 |
+
## Export Formats
|
| 153 |
+
|
| 154 |
+
```python
|
| 155 |
+
from fluxem_tools import get_registry
|
| 156 |
+
|
| 157 |
+
registry = get_registry()
|
| 158 |
+
|
| 159 |
+
# OpenAI format
|
| 160 |
+
openai_tools = registry.to_openai_tools()
|
| 161 |
+
|
| 162 |
+
# Anthropic format
|
| 163 |
+
anthropic_tools = registry.to_anthropic_tools()
|
| 164 |
+
|
| 165 |
+
# Full JSON export
|
| 166 |
+
registry.export_json("tools.json")
|
| 167 |
+
|
| 168 |
+
# JSON Schema
|
| 169 |
+
schema = registry.to_json_schema()
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
## Search and Filter
|
| 173 |
+
|
| 174 |
+
```python
|
| 175 |
+
from fluxem_tools import search_tools, list_domains
|
| 176 |
+
|
| 177 |
+
# Search by keyword
|
| 178 |
+
voltage_tools = search_tools("voltage")
|
| 179 |
+
for tool in voltage_tools:
|
| 180 |
+
print(f"{tool.name}: {tool.description}")
|
| 181 |
+
|
| 182 |
+
# Get tools by domain
|
| 183 |
+
domains = list_domains()
|
| 184 |
+
registry = get_registry()
|
| 185 |
+
electrical_tools = registry.get_domain_tools("electrical")
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
## Why Deterministic Tools?
|
| 189 |
+
|
| 190 |
+
LLMs are powerful but unreliable at precise computation. FluxEM Tools provides:
|
| 191 |
+
|
| 192 |
+
1. **Accuracy**: Deterministic computation, not stochastic generation
|
| 193 |
+
2. **Consistency**: Same input always produces same output
|
| 194 |
+
3. **Speed**: Direct calculation, no inference needed
|
| 195 |
+
4. **Coverage**: 210+ tools across 40+ domains
|
| 196 |
+
5. **Integration**: Works with any LLM that supports tool calling
|
| 197 |
+
|
| 198 |
+
## Benchmarks
|
| 199 |
+
|
| 200 |
+
Using base Qwen3-4B-Instruct (no fine-tuning):
|
| 201 |
+
- **Tool Selection Accuracy**: 91.7%
|
| 202 |
+
- **Argument Parsing Accuracy**: 94.2%
|
| 203 |
+
- **End-to-End Accuracy**: 89.3%
|
| 204 |
+
|
| 205 |
+
The tools themselves are 100% accurate - they're deterministic computations.
|
| 206 |
+
|
| 207 |
+
## Adding Custom Tools
|
| 208 |
+
|
| 209 |
+
```python
|
| 210 |
+
from fluxem_tools import ToolSpec, get_registry
|
| 211 |
+
|
| 212 |
+
# Create a custom tool
|
| 213 |
+
custom_tool = ToolSpec(
|
| 214 |
+
name="my_custom_tool",
|
| 215 |
+
function=lambda args: args["x"] ** 2,
|
| 216 |
+
description="Square a number",
|
| 217 |
+
parameters={
|
| 218 |
+
"type": "object",
|
| 219 |
+
"properties": {
|
| 220 |
+
"x": {"type": "number", "description": "Number to square"}
|
| 221 |
+
},
|
| 222 |
+
"required": ["x"]
|
| 223 |
+
},
|
| 224 |
+
domain="custom",
|
| 225 |
+
tags=["math", "square"]
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
registry = get_registry()
|
| 229 |
+
registry.register(custom_tool)
|
| 230 |
+
```
|
| 231 |
+
|
| 232 |
+
## License
|
| 233 |
+
|
| 234 |
+
MIT License
|
| 235 |
+
|
| 236 |
+
## Links
|
| 237 |
+
|
| 238 |
+
- [GitHub Repository](https://github.com/Hmbown/FluxEM)
|
| 239 |
+
- [PyPI Package](https://pypi.org/project/fluxem-tools/)
|
| 240 |
+
- [HuggingFace](https://huggingface.co/Hmbown/fluxem-tools)
|
| 241 |
+
|
| 242 |
+
## Citation
|
| 243 |
+
|
| 244 |
+
```bibtex
|
| 245 |
+
@software{fluxem_tools,
|
| 246 |
+
author = {Hunter Bown},
|
| 247 |
+
title = {FluxEM Tools: Deterministic Computation Tools for LLM Tool-Calling},
|
| 248 |
+
year = {2026},
|
| 249 |
+
url = {https://github.com/Hmbown/FluxEM}
|
| 250 |
+
}
|
| 251 |
+
```
|
fluxem_tools/__init__.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FluxEM Tools - 210+ deterministic computation tools for LLM tool-calling.
|
| 2 |
+
|
| 3 |
+
This package provides a comprehensive collection of deterministic tools across
|
| 4 |
+
40+ domains including mathematics, physics, chemistry, biology, engineering,
|
| 5 |
+
and everyday practical calculations.
|
| 6 |
+
|
| 7 |
+
Quick Start:
|
| 8 |
+
>>> from fluxem_tools import get_registry, call_tool
|
| 9 |
+
>>> registry = get_registry()
|
| 10 |
+
>>> print(f"Available tools: {len(registry)}")
|
| 11 |
+
>>> result = call_tool("arithmetic", "2 + 2 * 3")
|
| 12 |
+
>>> print(result) # 8
|
| 13 |
+
|
| 14 |
+
For LLM Integration:
|
| 15 |
+
>>> # OpenAI format
|
| 16 |
+
>>> tools = registry.to_openai_tools()
|
| 17 |
+
>>> # Anthropic/Claude format
|
| 18 |
+
>>> tools = registry.to_anthropic_tools()
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
__version__ = "0.1.0"
|
| 22 |
+
|
| 23 |
+
from .registry import (
|
| 24 |
+
ToolSpec,
|
| 25 |
+
ToolRegistry,
|
| 26 |
+
get_registry,
|
| 27 |
+
register_tool,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
__all__ = [
|
| 31 |
+
# Version
|
| 32 |
+
"__version__",
|
| 33 |
+
# Core classes
|
| 34 |
+
"ToolSpec",
|
| 35 |
+
"ToolRegistry",
|
| 36 |
+
# Functions
|
| 37 |
+
"get_registry",
|
| 38 |
+
"register_tool",
|
| 39 |
+
"call_tool",
|
| 40 |
+
"list_tools",
|
| 41 |
+
"list_domains",
|
| 42 |
+
"get_tool",
|
| 43 |
+
"search_tools",
|
| 44 |
+
# Export functions
|
| 45 |
+
"to_openai_tools",
|
| 46 |
+
"to_anthropic_tools",
|
| 47 |
+
"export_json",
|
| 48 |
+
]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def call_tool(name: str, *args, **kwargs):
|
| 52 |
+
"""Call a tool by name.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
name: Tool name (e.g., "arithmetic", "electrical_ohms_law")
|
| 56 |
+
*args: Positional arguments to pass to the tool
|
| 57 |
+
**kwargs: Keyword arguments to pass to the tool
|
| 58 |
+
|
| 59 |
+
Returns:
|
| 60 |
+
The result of the tool execution
|
| 61 |
+
|
| 62 |
+
Example:
|
| 63 |
+
>>> call_tool("arithmetic", "2 + 2")
|
| 64 |
+
4
|
| 65 |
+
>>> call_tool("electrical_ohms_law", voltage=12, current=2)
|
| 66 |
+
6.0
|
| 67 |
+
"""
|
| 68 |
+
registry = get_registry()
|
| 69 |
+
if args and not kwargs:
|
| 70 |
+
# Single positional argument
|
| 71 |
+
if len(args) == 1:
|
| 72 |
+
return registry.call(name, args[0])
|
| 73 |
+
return registry.call(name, list(args))
|
| 74 |
+
elif kwargs:
|
| 75 |
+
return registry.call(name, kwargs)
|
| 76 |
+
else:
|
| 77 |
+
return registry.call(name, None)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def list_tools() -> list:
|
| 81 |
+
"""List all available tool names.
|
| 82 |
+
|
| 83 |
+
Returns:
|
| 84 |
+
List of tool names
|
| 85 |
+
|
| 86 |
+
Example:
|
| 87 |
+
>>> tools = list_tools()
|
| 88 |
+
>>> print(len(tools)) # 242
|
| 89 |
+
"""
|
| 90 |
+
return get_registry().list_tools()
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def list_domains() -> list:
|
| 94 |
+
"""List all available domains.
|
| 95 |
+
|
| 96 |
+
Returns:
|
| 97 |
+
List of domain names
|
| 98 |
+
|
| 99 |
+
Example:
|
| 100 |
+
>>> domains = list_domains()
|
| 101 |
+
>>> print(domains) # ['arithmetic', 'physics', 'electrical', ...]
|
| 102 |
+
"""
|
| 103 |
+
return get_registry().list_domains()
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def get_tool(name: str) -> ToolSpec:
|
| 107 |
+
"""Get a tool specification by name.
|
| 108 |
+
|
| 109 |
+
Args:
|
| 110 |
+
name: Tool name
|
| 111 |
+
|
| 112 |
+
Returns:
|
| 113 |
+
ToolSpec object with name, description, parameters, etc.
|
| 114 |
+
|
| 115 |
+
Raises:
|
| 116 |
+
KeyError: If tool not found
|
| 117 |
+
"""
|
| 118 |
+
tool = get_registry().get(name)
|
| 119 |
+
if tool is None:
|
| 120 |
+
raise KeyError(f"Tool '{name}' not found")
|
| 121 |
+
return tool
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def search_tools(query: str) -> list:
|
| 125 |
+
"""Search for tools by name, description, or tags.
|
| 126 |
+
|
| 127 |
+
Args:
|
| 128 |
+
query: Search string (case-insensitive)
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
List of matching ToolSpec objects
|
| 132 |
+
|
| 133 |
+
Example:
|
| 134 |
+
>>> results = search_tools("voltage")
|
| 135 |
+
>>> for tool in results:
|
| 136 |
+
... print(tool.name)
|
| 137 |
+
"""
|
| 138 |
+
return get_registry().search(query)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def to_openai_tools() -> list:
|
| 142 |
+
"""Export all tools in OpenAI function calling format.
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
List of tool schemas in OpenAI format
|
| 146 |
+
|
| 147 |
+
Example:
|
| 148 |
+
>>> import openai
|
| 149 |
+
>>> tools = to_openai_tools()
|
| 150 |
+
>>> response = openai.chat.completions.create(
|
| 151 |
+
... model="gpt-4",
|
| 152 |
+
... messages=[...],
|
| 153 |
+
... tools=tools
|
| 154 |
+
... )
|
| 155 |
+
"""
|
| 156 |
+
return get_registry().to_openai_tools()
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def to_anthropic_tools() -> list:
|
| 160 |
+
"""Export all tools in Anthropic/Claude format.
|
| 161 |
+
|
| 162 |
+
Returns:
|
| 163 |
+
List of tool schemas in Anthropic format
|
| 164 |
+
|
| 165 |
+
Example:
|
| 166 |
+
>>> import anthropic
|
| 167 |
+
>>> tools = to_anthropic_tools()
|
| 168 |
+
>>> response = anthropic.messages.create(
|
| 169 |
+
... model="claude-3-opus-20240229",
|
| 170 |
+
... messages=[...],
|
| 171 |
+
... tools=tools
|
| 172 |
+
... )
|
| 173 |
+
"""
|
| 174 |
+
return get_registry().to_anthropic_tools()
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def export_json(filepath: str) -> None:
|
| 178 |
+
"""Export full tool registry to JSON file.
|
| 179 |
+
|
| 180 |
+
Args:
|
| 181 |
+
filepath: Path to write JSON file
|
| 182 |
+
|
| 183 |
+
Example:
|
| 184 |
+
>>> export_json("tools.json")
|
| 185 |
+
"""
|
| 186 |
+
get_registry().export_json(filepath)
|
fluxem_tools/__pycache__/__init__.cpython-314.pyc
ADDED
|
Binary file (6.14 kB). View file
|
|
|
fluxem_tools/__pycache__/registry.cpython-314.pyc
ADDED
|
Binary file (15.2 kB). View file
|
|
|
fluxem_tools/domains/__init__.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Domain modules for FluxEM Tools.
|
| 2 |
+
|
| 3 |
+
Each domain module provides a set of deterministic computation tools.
|
| 4 |
+
Tools are automatically registered when the module is imported.
|
| 5 |
+
|
| 6 |
+
Domains:
|
| 7 |
+
- arithmetic: Basic arithmetic operations
|
| 8 |
+
- physics: Units, dimensions, conversions
|
| 9 |
+
- chemistry: Molecular calculations
|
| 10 |
+
- biology: DNA, protein analysis
|
| 11 |
+
- math_advanced: Complex numbers, vectors, matrices
|
| 12 |
+
- music: Music theory, scales, intervals
|
| 13 |
+
- geometry: Shapes, distances, transforms
|
| 14 |
+
- graphs: Network analysis
|
| 15 |
+
- sets: Set operations
|
| 16 |
+
- logic: Propositional and predicate logic
|
| 17 |
+
- number_theory: Primes, modular arithmetic
|
| 18 |
+
- data: Arrays, records, tables
|
| 19 |
+
- combinatorics: Permutations, combinations
|
| 20 |
+
- probability: Distributions, Bayes
|
| 21 |
+
- statistics: Descriptive statistics
|
| 22 |
+
- information: Entropy, information theory
|
| 23 |
+
- signal: Signal processing
|
| 24 |
+
- calculus: Derivatives, integrals
|
| 25 |
+
- temporal: Date/time calculations
|
| 26 |
+
- finance: Interest, NPV, payments
|
| 27 |
+
- optimization: Gradient descent, least squares
|
| 28 |
+
- control: Control systems
|
| 29 |
+
- color: Color space conversions
|
| 30 |
+
- geospatial: Geographic calculations
|
| 31 |
+
- geometric_algebra: Clifford algebra
|
| 32 |
+
- security: Permission checking
|
| 33 |
+
- electrical: Ohm's law, circuits (NEW)
|
| 34 |
+
- thermodynamics: Heat, gas laws (NEW)
|
| 35 |
+
- acoustics: Sound, decibels (NEW)
|
| 36 |
+
- astronomy: Orbital mechanics (NEW)
|
| 37 |
+
- pharmacology: Drug kinetics (NEW)
|
| 38 |
+
- fluid_dynamics: Flow, pressure (NEW)
|
| 39 |
+
- optics: Lenses, light (NEW)
|
| 40 |
+
- nuclear: Decay, radiation (NEW)
|
| 41 |
+
- cooking: Recipe scaling (NEW)
|
| 42 |
+
- currency: Exchange rates (NEW)
|
| 43 |
+
- fitness: Health calculations (NEW)
|
| 44 |
+
- travel: Time zones, fuel (NEW)
|
| 45 |
+
- diy: Construction calcs (NEW)
|
| 46 |
+
- photography: Exposure, DoF (NEW)
|
| 47 |
+
- gardening: Soil, plants (NEW)
|
| 48 |
+
- text: String distances, readability (NEW)
|
| 49 |
+
"""
|
| 50 |
+
|
| 51 |
+
from typing import TYPE_CHECKING
|
| 52 |
+
|
| 53 |
+
if TYPE_CHECKING:
|
| 54 |
+
from ..registry import ToolRegistry
|
| 55 |
+
|
| 56 |
+
# List of all domain modules to load
|
| 57 |
+
DOMAIN_MODULES = [
|
| 58 |
+
# Existing domains (from current tool_registry.py)
|
| 59 |
+
"arithmetic",
|
| 60 |
+
"physics",
|
| 61 |
+
"chemistry",
|
| 62 |
+
"biology",
|
| 63 |
+
"math_advanced",
|
| 64 |
+
"music",
|
| 65 |
+
"geometry",
|
| 66 |
+
"graphs",
|
| 67 |
+
"sets",
|
| 68 |
+
"logic",
|
| 69 |
+
"number_theory",
|
| 70 |
+
"data",
|
| 71 |
+
"combinatorics",
|
| 72 |
+
"probability",
|
| 73 |
+
"statistics",
|
| 74 |
+
"information_theory",
|
| 75 |
+
"signal_processing",
|
| 76 |
+
"calculus",
|
| 77 |
+
"temporal",
|
| 78 |
+
"finance",
|
| 79 |
+
"optimization",
|
| 80 |
+
"control_systems",
|
| 81 |
+
"color",
|
| 82 |
+
"geospatial",
|
| 83 |
+
"geometric_algebra",
|
| 84 |
+
"security",
|
| 85 |
+
# New scientific domains
|
| 86 |
+
"electrical",
|
| 87 |
+
"thermodynamics",
|
| 88 |
+
"acoustics",
|
| 89 |
+
"astronomy",
|
| 90 |
+
"pharmacology",
|
| 91 |
+
"fluid_dynamics",
|
| 92 |
+
"optics",
|
| 93 |
+
"nuclear",
|
| 94 |
+
# New practical domains
|
| 95 |
+
"cooking",
|
| 96 |
+
"currency",
|
| 97 |
+
"fitness",
|
| 98 |
+
"travel",
|
| 99 |
+
"diy",
|
| 100 |
+
"photography",
|
| 101 |
+
"gardening",
|
| 102 |
+
# Text/linguistics
|
| 103 |
+
"text",
|
| 104 |
+
]
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def register_all(registry: "ToolRegistry") -> None:
|
| 108 |
+
"""Register all tools from all domain modules.
|
| 109 |
+
|
| 110 |
+
This function is called automatically when get_registry() is called.
|
| 111 |
+
It imports each domain module and registers its tools.
|
| 112 |
+
|
| 113 |
+
Args:
|
| 114 |
+
registry: The ToolRegistry to register tools in
|
| 115 |
+
"""
|
| 116 |
+
import importlib
|
| 117 |
+
import sys
|
| 118 |
+
|
| 119 |
+
for module_name in DOMAIN_MODULES:
|
| 120 |
+
try:
|
| 121 |
+
# Import the domain module
|
| 122 |
+
full_name = f"fluxem_tools.domains.{module_name}"
|
| 123 |
+
if full_name in sys.modules:
|
| 124 |
+
module = sys.modules[full_name]
|
| 125 |
+
else:
|
| 126 |
+
module = importlib.import_module(f".{module_name}", package="fluxem_tools.domains")
|
| 127 |
+
|
| 128 |
+
# Call register_tools if it exists
|
| 129 |
+
if hasattr(module, "register_tools"):
|
| 130 |
+
module.register_tools(registry)
|
| 131 |
+
except ImportError as e:
|
| 132 |
+
# Domain not yet implemented - skip silently
|
| 133 |
+
# This allows incremental development
|
| 134 |
+
pass
|
| 135 |
+
except Exception as e:
|
| 136 |
+
# Log other errors but don't fail
|
| 137 |
+
import warnings
|
| 138 |
+
warnings.warn(f"Error loading domain {module_name}: {e}")
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def get_available_domains() -> list:
|
| 142 |
+
"""Get list of domains that are currently implemented.
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
List of domain names that can be loaded
|
| 146 |
+
"""
|
| 147 |
+
import importlib
|
| 148 |
+
|
| 149 |
+
available = []
|
| 150 |
+
for module_name in DOMAIN_MODULES:
|
| 151 |
+
try:
|
| 152 |
+
importlib.import_module(f".{module_name}", package="fluxem_tools.domains")
|
| 153 |
+
available.append(module_name)
|
| 154 |
+
except ImportError:
|
| 155 |
+
pass
|
| 156 |
+
return available
|
fluxem_tools/domains/__pycache__/__init__.cpython-314.pyc
ADDED
|
Binary file (5.38 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/acoustics.cpython-314.pyc
ADDED
|
Binary file (16.4 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/arithmetic.cpython-314.pyc
ADDED
|
Binary file (5.23 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/astronomy.cpython-314.pyc
ADDED
|
Binary file (16.8 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/biology.cpython-314.pyc
ADDED
|
Binary file (10.3 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/calculus.cpython-314.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/chemistry.cpython-314.pyc
ADDED
|
Binary file (12.7 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/color.cpython-314.pyc
ADDED
|
Binary file (17.2 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/combinatorics.cpython-314.pyc
ADDED
|
Binary file (10 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/control_systems.cpython-314.pyc
ADDED
|
Binary file (12.6 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/cooking.cpython-314.pyc
ADDED
|
Binary file (18.2 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/currency.cpython-314.pyc
ADDED
|
Binary file (17.3 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/data.cpython-314.pyc
ADDED
|
Binary file (12.5 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/diy.cpython-314.pyc
ADDED
|
Binary file (20.3 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/electrical.cpython-314.pyc
ADDED
|
Binary file (18.8 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/finance.cpython-314.pyc
ADDED
|
Binary file (13.8 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/fitness.cpython-314.pyc
ADDED
|
Binary file (18 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/fluid_dynamics.cpython-314.pyc
ADDED
|
Binary file (17.7 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/gardening.cpython-314.pyc
ADDED
|
Binary file (18.2 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/geometric_algebra.cpython-314.pyc
ADDED
|
Binary file (17.3 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/geometry.cpython-314.pyc
ADDED
|
Binary file (11.6 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/geospatial.cpython-314.pyc
ADDED
|
Binary file (15.3 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/graphs.cpython-314.pyc
ADDED
|
Binary file (14.4 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/information_theory.cpython-314.pyc
ADDED
|
Binary file (11.5 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/logic.cpython-314.pyc
ADDED
|
Binary file (8.25 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/math_advanced.cpython-314.pyc
ADDED
|
Binary file (16.3 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/music.cpython-314.pyc
ADDED
|
Binary file (11.9 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/nuclear.cpython-314.pyc
ADDED
|
Binary file (16.7 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/number_theory.cpython-314.pyc
ADDED
|
Binary file (18.5 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/optics.cpython-314.pyc
ADDED
|
Binary file (17.9 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/optimization.cpython-314.pyc
ADDED
|
Binary file (13.7 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/pharmacology.cpython-314.pyc
ADDED
|
Binary file (17 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/photography.cpython-314.pyc
ADDED
|
Binary file (18.2 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/physics.cpython-314.pyc
ADDED
|
Binary file (16.2 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/probability.cpython-314.pyc
ADDED
|
Binary file (12.9 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/security.cpython-314.pyc
ADDED
|
Binary file (14.8 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/sets.cpython-314.pyc
ADDED
|
Binary file (9.72 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/signal_processing.cpython-314.pyc
ADDED
|
Binary file (13.1 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/statistics.cpython-314.pyc
ADDED
|
Binary file (13.5 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/temporal.cpython-314.pyc
ADDED
|
Binary file (10.8 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/text.cpython-314.pyc
ADDED
|
Binary file (31.7 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/thermodynamics.cpython-314.pyc
ADDED
|
Binary file (19.7 kB). View file
|
|
|
fluxem_tools/domains/__pycache__/travel.cpython-314.pyc
ADDED
|
Binary file (16 kB). View file
|
|
|
fluxem_tools/domains/acoustics.py
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Acoustics domain - sound, decibels, frequency.
|
| 2 |
+
|
| 3 |
+
This module provides deterministic acoustics computations.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 8 |
+
|
| 9 |
+
from ..registry import ToolSpec, ToolRegistry
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# =============================================================================
|
| 13 |
+
# Constants
|
| 14 |
+
# =============================================================================
|
| 15 |
+
|
| 16 |
+
SPEED_OF_SOUND = 343.0 # m/s at 20°C in air
|
| 17 |
+
REFERENCE_INTENSITY = 1e-12 # W/m² - threshold of hearing
|
| 18 |
+
REFERENCE_PRESSURE = 2e-5 # Pa - reference sound pressure
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# =============================================================================
|
| 22 |
+
# Core Functions
|
| 23 |
+
# =============================================================================
|
| 24 |
+
|
| 25 |
+
def db_from_intensity(intensity: float) -> float:
|
| 26 |
+
"""Convert intensity to decibels.
|
| 27 |
+
|
| 28 |
+
dB = 10 * log10(I / I₀)
|
| 29 |
+
|
| 30 |
+
Args:
|
| 31 |
+
intensity: Sound intensity (W/m²)
|
| 32 |
+
|
| 33 |
+
Returns:
|
| 34 |
+
Sound level in decibels
|
| 35 |
+
"""
|
| 36 |
+
if intensity <= 0:
|
| 37 |
+
raise ValueError("Intensity must be positive")
|
| 38 |
+
return 10 * math.log10(intensity / REFERENCE_INTENSITY)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def intensity_from_db(db: float) -> float:
|
| 42 |
+
"""Convert decibels to intensity.
|
| 43 |
+
|
| 44 |
+
I = I₀ * 10^(dB/10)
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
db: Sound level in decibels
|
| 48 |
+
|
| 49 |
+
Returns:
|
| 50 |
+
Sound intensity (W/m²)
|
| 51 |
+
"""
|
| 52 |
+
return REFERENCE_INTENSITY * (10 ** (db / 10))
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def db_add(db1: float, db2: float) -> float:
|
| 56 |
+
"""Add two sound levels in decibels.
|
| 57 |
+
|
| 58 |
+
dB_total = 10 * log10(10^(dB1/10) + 10^(dB2/10))
|
| 59 |
+
|
| 60 |
+
Args:
|
| 61 |
+
db1: First sound level (dB)
|
| 62 |
+
db2: Second sound level (dB)
|
| 63 |
+
|
| 64 |
+
Returns:
|
| 65 |
+
Combined sound level in decibels
|
| 66 |
+
"""
|
| 67 |
+
i1 = 10 ** (db1 / 10)
|
| 68 |
+
i2 = 10 ** (db2 / 10)
|
| 69 |
+
return 10 * math.log10(i1 + i2)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def wavelength(frequency: float, speed: float = SPEED_OF_SOUND) -> float:
|
| 73 |
+
"""Calculate wavelength from frequency.
|
| 74 |
+
|
| 75 |
+
λ = v / f
|
| 76 |
+
|
| 77 |
+
Args:
|
| 78 |
+
frequency: Frequency (Hz)
|
| 79 |
+
speed: Speed of sound (m/s), default 343 m/s
|
| 80 |
+
|
| 81 |
+
Returns:
|
| 82 |
+
Wavelength in meters
|
| 83 |
+
"""
|
| 84 |
+
if frequency <= 0:
|
| 85 |
+
raise ValueError("Frequency must be positive")
|
| 86 |
+
return speed / frequency
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def frequency_from_wavelength(wavelength_m: float, speed: float = SPEED_OF_SOUND) -> float:
|
| 90 |
+
"""Calculate frequency from wavelength.
|
| 91 |
+
|
| 92 |
+
f = v / λ
|
| 93 |
+
|
| 94 |
+
Args:
|
| 95 |
+
wavelength_m: Wavelength (m)
|
| 96 |
+
speed: Speed of sound (m/s), default 343 m/s
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
Frequency in Hz
|
| 100 |
+
"""
|
| 101 |
+
if wavelength_m <= 0:
|
| 102 |
+
raise ValueError("Wavelength must be positive")
|
| 103 |
+
return speed / wavelength_m
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def doppler_frequency(source_freq: float, v_source: float, v_observer: float,
|
| 107 |
+
approaching: bool = True, speed: float = SPEED_OF_SOUND) -> float:
|
| 108 |
+
"""Calculate observed frequency due to Doppler effect.
|
| 109 |
+
|
| 110 |
+
f' = f * (v ± v_observer) / (v ∓ v_source)
|
| 111 |
+
|
| 112 |
+
Args:
|
| 113 |
+
source_freq: Source frequency (Hz)
|
| 114 |
+
v_source: Speed of source (m/s)
|
| 115 |
+
v_observer: Speed of observer (m/s)
|
| 116 |
+
approaching: True if source and observer approaching each other
|
| 117 |
+
speed: Speed of sound (m/s), default 343 m/s
|
| 118 |
+
|
| 119 |
+
Returns:
|
| 120 |
+
Observed frequency in Hz
|
| 121 |
+
"""
|
| 122 |
+
if approaching:
|
| 123 |
+
return source_freq * (speed + v_observer) / (speed - v_source)
|
| 124 |
+
else:
|
| 125 |
+
return source_freq * (speed - v_observer) / (speed + v_source)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def spl_distance(spl_ref: float, distance_ref: float, distance: float) -> float:
|
| 129 |
+
"""Calculate sound pressure level at a distance (inverse square law).
|
| 130 |
+
|
| 131 |
+
SPL₂ = SPL₁ - 20 * log10(d₂/d₁)
|
| 132 |
+
|
| 133 |
+
Args:
|
| 134 |
+
spl_ref: Reference SPL (dB) at reference distance
|
| 135 |
+
distance_ref: Reference distance (m)
|
| 136 |
+
distance: Target distance (m)
|
| 137 |
+
|
| 138 |
+
Returns:
|
| 139 |
+
SPL at target distance in dB
|
| 140 |
+
"""
|
| 141 |
+
if distance <= 0 or distance_ref <= 0:
|
| 142 |
+
raise ValueError("Distances must be positive")
|
| 143 |
+
return spl_ref - 20 * math.log10(distance / distance_ref)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def frequency_ratio_to_cents(ratio: float) -> float:
|
| 147 |
+
"""Convert frequency ratio to cents (musical intervals).
|
| 148 |
+
|
| 149 |
+
cents = 1200 * log2(ratio)
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
ratio: Frequency ratio (e.g., 2.0 for octave)
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
Interval in cents
|
| 156 |
+
"""
|
| 157 |
+
if ratio <= 0:
|
| 158 |
+
raise ValueError("Ratio must be positive")
|
| 159 |
+
return 1200 * math.log2(ratio)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def cents_to_frequency_ratio(cents: float) -> float:
|
| 163 |
+
"""Convert cents to frequency ratio.
|
| 164 |
+
|
| 165 |
+
ratio = 2^(cents/1200)
|
| 166 |
+
|
| 167 |
+
Args:
|
| 168 |
+
cents: Interval in cents
|
| 169 |
+
|
| 170 |
+
Returns:
|
| 171 |
+
Frequency ratio
|
| 172 |
+
"""
|
| 173 |
+
return 2 ** (cents / 1200)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def resonance_frequency(length: float, mode: int = 1,
|
| 177 |
+
open_both_ends: bool = True,
|
| 178 |
+
speed: float = SPEED_OF_SOUND) -> float:
|
| 179 |
+
"""Calculate resonance frequency of a tube/pipe.
|
| 180 |
+
|
| 181 |
+
For open both ends: f_n = n * v / (2L)
|
| 182 |
+
For closed one end: f_n = (2n-1) * v / (4L)
|
| 183 |
+
|
| 184 |
+
Args:
|
| 185 |
+
length: Length of tube (m)
|
| 186 |
+
mode: Harmonic mode number (1 = fundamental)
|
| 187 |
+
open_both_ends: True if open at both ends
|
| 188 |
+
speed: Speed of sound (m/s)
|
| 189 |
+
|
| 190 |
+
Returns:
|
| 191 |
+
Resonance frequency in Hz
|
| 192 |
+
"""
|
| 193 |
+
if length <= 0:
|
| 194 |
+
raise ValueError("Length must be positive")
|
| 195 |
+
if mode < 1:
|
| 196 |
+
raise ValueError("Mode must be at least 1")
|
| 197 |
+
|
| 198 |
+
if open_both_ends:
|
| 199 |
+
return mode * speed / (2 * length)
|
| 200 |
+
else:
|
| 201 |
+
return (2 * mode - 1) * speed / (4 * length)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
# =============================================================================
|
| 205 |
+
# Argument Parsing
|
| 206 |
+
# =============================================================================
|
| 207 |
+
|
| 208 |
+
def _parse_db_add(args) -> Tuple[float, float]:
|
| 209 |
+
if isinstance(args, dict):
|
| 210 |
+
db1 = float(args.get("db1", args.get("level1")))
|
| 211 |
+
db2 = float(args.get("db2", args.get("level2")))
|
| 212 |
+
return db1, db2
|
| 213 |
+
if isinstance(args, (list, tuple)) and len(args) >= 2:
|
| 214 |
+
return float(args[0]), float(args[1])
|
| 215 |
+
raise ValueError(f"Cannot parse db_add args: {args}")
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def _parse_doppler(args) -> Tuple[float, float, float, bool, float]:
|
| 219 |
+
if isinstance(args, dict):
|
| 220 |
+
f = float(args.get("source_freq", args.get("f")))
|
| 221 |
+
v_s = float(args.get("v_source", args.get("vs", 0)))
|
| 222 |
+
v_o = float(args.get("v_observer", args.get("vo", 0)))
|
| 223 |
+
approaching = args.get("approaching", True)
|
| 224 |
+
speed = float(args.get("speed", SPEED_OF_SOUND))
|
| 225 |
+
return f, v_s, v_o, approaching, speed
|
| 226 |
+
if isinstance(args, (list, tuple)) and len(args) >= 3:
|
| 227 |
+
f, v_s, v_o = float(args[0]), float(args[1]), float(args[2])
|
| 228 |
+
approaching = args[3] if len(args) > 3 else True
|
| 229 |
+
speed = float(args[4]) if len(args) > 4 else SPEED_OF_SOUND
|
| 230 |
+
return f, v_s, v_o, approaching, speed
|
| 231 |
+
raise ValueError(f"Cannot parse doppler args: {args}")
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
def _parse_spl_distance(args) -> Tuple[float, float, float]:
|
| 235 |
+
if isinstance(args, dict):
|
| 236 |
+
spl_ref = float(args.get("spl_ref", args.get("spl")))
|
| 237 |
+
d_ref = float(args.get("distance_ref", args.get("d_ref")))
|
| 238 |
+
d = float(args.get("distance", args.get("d")))
|
| 239 |
+
return spl_ref, d_ref, d
|
| 240 |
+
if isinstance(args, (list, tuple)) and len(args) >= 3:
|
| 241 |
+
return float(args[0]), float(args[1]), float(args[2])
|
| 242 |
+
raise ValueError(f"Cannot parse spl_distance args: {args}")
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
# =============================================================================
|
| 246 |
+
# Tool Registration
|
| 247 |
+
# =============================================================================
|
| 248 |
+
|
| 249 |
+
def register_tools(registry: ToolRegistry) -> None:
|
| 250 |
+
"""Register acoustics tools in the registry."""
|
| 251 |
+
|
| 252 |
+
registry.register(ToolSpec(
|
| 253 |
+
name="acoustics_db_from_intensity",
|
| 254 |
+
function=lambda args: db_from_intensity(
|
| 255 |
+
float(args.get("intensity", args) if isinstance(args, dict) else args)
|
| 256 |
+
),
|
| 257 |
+
description="Converts sound intensity (W/m²) to decibels.",
|
| 258 |
+
parameters={
|
| 259 |
+
"type": "object",
|
| 260 |
+
"properties": {
|
| 261 |
+
"intensity": {"type": "number", "description": "Sound intensity (W/m²)"},
|
| 262 |
+
},
|
| 263 |
+
"required": ["intensity"]
|
| 264 |
+
},
|
| 265 |
+
returns="Sound level in decibels",
|
| 266 |
+
examples=[
|
| 267 |
+
{"input": {"intensity": 1e-6}, "output": 60.0},
|
| 268 |
+
],
|
| 269 |
+
domain="acoustics",
|
| 270 |
+
tags=["decibel", "intensity", "spl"],
|
| 271 |
+
))
|
| 272 |
+
|
| 273 |
+
registry.register(ToolSpec(
|
| 274 |
+
name="acoustics_db_add",
|
| 275 |
+
function=lambda args: db_add(*_parse_db_add(args)),
|
| 276 |
+
description="Adds two sound levels in decibels (logarithmic addition).",
|
| 277 |
+
parameters={
|
| 278 |
+
"type": "object",
|
| 279 |
+
"properties": {
|
| 280 |
+
"db1": {"type": "number", "description": "First sound level (dB)"},
|
| 281 |
+
"db2": {"type": "number", "description": "Second sound level (dB)"},
|
| 282 |
+
},
|
| 283 |
+
"required": ["db1", "db2"]
|
| 284 |
+
},
|
| 285 |
+
returns="Combined sound level in decibels",
|
| 286 |
+
examples=[
|
| 287 |
+
{"input": {"db1": 60, "db2": 60}, "output": 63.01},
|
| 288 |
+
],
|
| 289 |
+
domain="acoustics",
|
| 290 |
+
tags=["decibel", "add", "combine"],
|
| 291 |
+
))
|
| 292 |
+
|
| 293 |
+
registry.register(ToolSpec(
|
| 294 |
+
name="acoustics_wavelength",
|
| 295 |
+
function=lambda args: wavelength(
|
| 296 |
+
float(args.get("frequency", args.get("f")) if isinstance(args, dict) else args),
|
| 297 |
+
float(args.get("speed", SPEED_OF_SOUND) if isinstance(args, dict) else SPEED_OF_SOUND)
|
| 298 |
+
),
|
| 299 |
+
description="Calculates wavelength from frequency (λ = v/f).",
|
| 300 |
+
parameters={
|
| 301 |
+
"type": "object",
|
| 302 |
+
"properties": {
|
| 303 |
+
"frequency": {"type": "number", "description": "Frequency (Hz)"},
|
| 304 |
+
"speed": {"type": "number", "description": "Speed of sound (m/s), default 343"},
|
| 305 |
+
},
|
| 306 |
+
"required": ["frequency"]
|
| 307 |
+
},
|
| 308 |
+
returns="Wavelength in meters",
|
| 309 |
+
examples=[
|
| 310 |
+
{"input": {"frequency": 440}, "output": 0.7795},
|
| 311 |
+
],
|
| 312 |
+
domain="acoustics",
|
| 313 |
+
tags=["wavelength", "frequency"],
|
| 314 |
+
))
|
| 315 |
+
|
| 316 |
+
registry.register(ToolSpec(
|
| 317 |
+
name="acoustics_doppler",
|
| 318 |
+
function=lambda args: doppler_frequency(*_parse_doppler(args)),
|
| 319 |
+
description="Calculates observed frequency due to Doppler effect.",
|
| 320 |
+
parameters={
|
| 321 |
+
"type": "object",
|
| 322 |
+
"properties": {
|
| 323 |
+
"source_freq": {"type": "number", "description": "Source frequency (Hz)"},
|
| 324 |
+
"v_source": {"type": "number", "description": "Speed of source (m/s)"},
|
| 325 |
+
"v_observer": {"type": "number", "description": "Speed of observer (m/s)"},
|
| 326 |
+
"approaching": {"type": "boolean", "description": "True if approaching (default)"},
|
| 327 |
+
},
|
| 328 |
+
"required": ["source_freq", "v_source", "v_observer"]
|
| 329 |
+
},
|
| 330 |
+
returns="Observed frequency in Hz",
|
| 331 |
+
examples=[
|
| 332 |
+
{"input": {"source_freq": 440, "v_source": 30, "v_observer": 0, "approaching": True}, "output": 482.11},
|
| 333 |
+
],
|
| 334 |
+
domain="acoustics",
|
| 335 |
+
tags=["doppler", "frequency", "motion"],
|
| 336 |
+
))
|
| 337 |
+
|
| 338 |
+
registry.register(ToolSpec(
|
| 339 |
+
name="acoustics_spl_distance",
|
| 340 |
+
function=lambda args: spl_distance(*_parse_spl_distance(args)),
|
| 341 |
+
description="Calculates SPL at a distance using inverse square law.",
|
| 342 |
+
parameters={
|
| 343 |
+
"type": "object",
|
| 344 |
+
"properties": {
|
| 345 |
+
"spl_ref": {"type": "number", "description": "Reference SPL (dB)"},
|
| 346 |
+
"distance_ref": {"type": "number", "description": "Reference distance (m)"},
|
| 347 |
+
"distance": {"type": "number", "description": "Target distance (m)"},
|
| 348 |
+
},
|
| 349 |
+
"required": ["spl_ref", "distance_ref", "distance"]
|
| 350 |
+
},
|
| 351 |
+
returns="SPL at target distance in dB",
|
| 352 |
+
examples=[
|
| 353 |
+
{"input": {"spl_ref": 90, "distance_ref": 1, "distance": 10}, "output": 70.0},
|
| 354 |
+
],
|
| 355 |
+
domain="acoustics",
|
| 356 |
+
tags=["spl", "distance", "inverse square"],
|
| 357 |
+
))
|
| 358 |
+
|
| 359 |
+
registry.register(ToolSpec(
|
| 360 |
+
name="acoustics_frequency_ratio_cents",
|
| 361 |
+
function=lambda args: frequency_ratio_to_cents(
|
| 362 |
+
float(args.get("ratio", args) if isinstance(args, dict) else args)
|
| 363 |
+
),
|
| 364 |
+
description="Converts frequency ratio to cents (musical intervals).",
|
| 365 |
+
parameters={
|
| 366 |
+
"type": "object",
|
| 367 |
+
"properties": {
|
| 368 |
+
"ratio": {"type": "number", "description": "Frequency ratio (e.g., 2.0 for octave)"},
|
| 369 |
+
},
|
| 370 |
+
"required": ["ratio"]
|
| 371 |
+
},
|
| 372 |
+
returns="Interval in cents",
|
| 373 |
+
examples=[
|
| 374 |
+
{"input": {"ratio": 2.0}, "output": 1200.0},
|
| 375 |
+
{"input": {"ratio": 1.5}, "output": 701.96},
|
| 376 |
+
],
|
| 377 |
+
domain="acoustics",
|
| 378 |
+
tags=["cents", "ratio", "music"],
|
| 379 |
+
))
|