Metadata-Version: 2.5
Name: avada-eval
Version: 0.1.0
Summary: Safe, Decimal-exact expression evaluator for LLM tools, derived from simpleeval.
Project-URL: Homepage, https://github.com/VoldemortGin/avada-eval
Project-URL: Repository, https://github.com/VoldemortGin/avada-eval
Project-URL: Issues, https://github.com/VoldemortGin/avada-eval/issues
Author-email: lin han <mn2895566@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: calculator,decimal,eval,evaluator,expression,llm,sandbox,simpleeval,tool
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Software Development :: Interpreters
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: beartype>=0.20
Description-Content-Type: text/markdown

# avada-eval

**Safe, Decimal-exact expression evaluator for LLM tools, derived from simpleeval.**

安全、可扩展、给大模型当计算工具用的表达式求值器。派生自
[simpleeval](https://github.com/danthedeckie/simpleeval)（v1.0.8，commit
`3b30888d0d5954df8a11b7e5ef26d17e19a96c29`，MIT，© 2013-2026 Daniel Fairhead），
核心区别：**用 `decimal.Decimal` 代替 float 做精确计算**，并加固了资源耗尽（DoS）防护。

```bash
pip install avada-eval        # 或 uv add avada-eval
```

需要 Python ≥ 3.11；唯一运行时依赖 `beartype`（运行时类型检查，`AVADA_BEARTYPE_ON=false` 可关闭）。

## 快速示例

```python
from avada_eval import simple_eval, SimpleEval  # 替换 `from simpleeval import ...`

simple_eval("0.1 + 0.2")  # Decimal('0.3')
simple_eval("1 / 3")  # Decimal('0.3333333333333333333333333333')
simple_eval("2 ** 100")  # 1267650600228229401496703205376（int 精确）
simple_eval("price * qty", names={"price": 19.99, "qty": 3})  # Decimal('59.97')
```

## Decimal 语义（与 simpleeval 的差异）

| 项 | simpleeval | avada-eval |
|---|---|---|
| 小数字面量 | float | 按**源码原文**构造的 `Decimal`（`1.10` 保留尾零） |
| 整数字面量 | int | int（`**` `//` `%` 位运算与 Python 一致） |
| `/` | float | `Decimal`（int/int 亦然） |
| 负/小数指数 `**` | float | `Decimal`；Decimal 路径只限制指数，结果超出 `Emax` → `NumberTooHigh` |
| Decimal 的 `//` `%` | — | 取整向下（与 int/float 一致，而非 decimal 模块的截断） |
| 除零 | `ZeroDivisionError` | `DivisionByZero`（同时是 `InvalidExpression` 与 `ZeroDivisionError`） |
| 外部 float | 原样 | `float_policy="convert"`（默认，`Decimal(str(x))`）或 `"error"`（`FloatNotAllowed`） |
| 默认函数 | rand randint int float str | 另加 `decimal round abs min max sum`；`float()`/`rand()` 返回 Decimal |
| 精度/舍入 | — | `context=decimal.Context(...)`，每次 `eval` 在 `localcontext` 中执行 |

默认 context：28 位有效数字、ROUND_HALF_EVEN、`Emax/Emin = ±999999`，InvalidOperation /
DivisionByZero / Overflow 以异常抛出。设计决策见
[ADR 0002](https://github.com/VoldemortGin/avada-eval/blob/main/docs/adr/0002-decimal-semantics.md)。

## LLM 工具用法（`avada_eval.llm`）

```python
from avada_eval.llm import evaluate_for_llm, TOOL_DESCRIPTION

evaluate_for_llm("revenue * 12.5% + 1,000", {"revenue": 80000}).to_dict()
# {'ok': True, 'normalized': 'revenue * 0.125 + 1000', 'value': '11000.000', 'variables': {'revenue': '80000'}, ...}
```

- `evaluate_for_llm` 从不因表达式错误而抛异常：语法错误、未知名字、除零、安全拒绝等都以
  `ok=False` + `error` / `error_type` 返回，模型可据此重试；`TOOL_DESCRIPTION` 可直接作为工具描述。
- 财务函数：`pct_change(new, old)`、`cagr(end, start, years)`（均返回百分点）、`ratio(a, b)`、`avg(...)`。
- 千分位：逗号两侧**无空格**且分组合法（首组 1-3 位、其后每组 3 位）才视为千分位；逗号后带空格永远是参数分隔符（`max(1, 234)`）。
- 百分号：紧贴数字、且其后是结尾/空白/非操作数（`)` `,` `*` …）的 `%` 是百分号；`10%3`、`10 % 3` 仍是取模。

## 安全

安全模型继承 simpleeval 并**只加强不削弱**：禁止 `_`/`func_` 前缀属性、`format`/`format_map`
等危险方法、`DISALLOW_FUNCTIONS`、模块访问（需显式 `ModuleWrapper`）、lambda / import / 赋值。

资源耗尽防护在**分配或计算之前**按估算值拦截（修复了 simpleeval 中
`'%999999999d' % 1`、`f"{1:999999999}"`、`"a".center(999999999)`、`4000000 ** 4000000`、
`'%d' % 1e999999` 等先分配/计算再检查的问题）。上限为 `avada_eval.evaluator` 的模块级常量，调用时读取：

| 常量 | 默认 | 作用 |
|---|---|---|
| `MAX_STRING_LENGTH` | 100000 | 构造出的字符串/序列长度（容器按嵌套元素总数计） |
| `MAX_FORMAT_WIDTH` | 10000 | 格式化 width/precision 与 `center/ljust/rjust/zfill/expandtabs` 宽度 |
| `MAX_INT_BITS` | 15000 | 任何 int 的位数 |
| `MAX_POWER` / `MAX_SHIFT` | 4000000 / 10000 | `**` 底数与指数、移位量 |
| `MAX_POWER_BASE_DIGITS` | 1000 | Decimal 小数次幂的底数位数 |
| `MAX_COMPREHENSION_LENGTH` | 10000 | 推导式迭代次数（`EvalWithCompoundTypes`） |

```python
import avada_eval.evaluator as evaluator

evaluator.MAX_FORMAT_WIDTH = 1000  # 与 simpleeval 一样，直接改模块属性
```

完整排查清单与**拦不住的部分**（表达式长度、推导式总内存、自定义 context 等）见
[ADR 0003](https://github.com/VoldemortGin/avada-eval/blob/main/docs/adr/0003-resource-exhaustion-limits.md)。
对不可信输入，仍建议限制表达式长度，并在带超时与内存上限的子进程中求值。

## 命令行

```bash
avada "0.1 + 0.2"
avada "round(x * 3, 1)" -v x=0.05 --rounding ROUND_HALF_UP
avada "a + 1,000" -v a=1.5 --json
```

## 开发

`uv sync` 后，`make fmt` 做格式化，`./ci.sh`（从仓库根运行）是唯一的零警告质量门：
ruff format/check → mypy --strict → 结构检查 → 文档漂移 → pytest。

## 许可

MIT，见 [LICENSE](https://github.com/VoldemortGin/avada-eval/blob/main/LICENSE)（保留 simpleeval 原版权声明）。
