@nhtio/adk 0.1.0-master-463eb577 → 0.1.0-master-44ebe1d2
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.
- package/CHANGELOG.md +22 -0
- package/batteries/embeddings/openai/adapter.cjs +1 -1
- package/batteries/embeddings/openai/adapter.mjs +1 -1
- package/batteries/embeddings/webllm/adapter.cjs +1 -1
- package/batteries/embeddings/webllm/adapter.mjs +1 -1
- package/batteries/llm/openai_chat_completions/adapter.cjs +5 -5
- package/batteries/llm/openai_chat_completions/adapter.mjs +5 -5
- package/batteries/llm/openai_chat_completions/helpers.cjs +2 -2
- package/batteries/llm/openai_chat_completions/helpers.mjs +2 -2
- package/batteries/llm/openai_chat_completions/validation.cjs +1 -1
- package/batteries/llm/openai_chat_completions/validation.mjs +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.cjs +5 -5
- package/batteries/llm/webllm_chat_completions/adapter.mjs +5 -5
- package/batteries/llm/webllm_chat_completions/validation.cjs +1 -1
- package/batteries/llm/webllm_chat_completions/validation.mjs +1 -1
- package/batteries/storage/flydrive.cjs +1 -1
- package/batteries/storage/flydrive.mjs +1 -1
- package/batteries/storage/in_memory.cjs +1 -1
- package/batteries/storage/in_memory.mjs +1 -1
- package/batteries/storage/opfs.cjs +1 -1
- package/batteries/storage/opfs.mjs +1 -1
- package/batteries/tools/color.cjs +2 -2
- package/batteries/tools/color.mjs +2 -2
- package/batteries/tools/comparison.cjs +3 -3
- package/batteries/tools/comparison.mjs +3 -3
- package/batteries/tools/data_structure.cjs +3 -3
- package/batteries/tools/data_structure.mjs +3 -3
- package/batteries/tools/datetime_extended.cjs +2 -2
- package/batteries/tools/datetime_extended.mjs +2 -2
- package/batteries/tools/datetime_math.cjs +2 -2
- package/batteries/tools/datetime_math.mjs +2 -2
- package/batteries/tools/encoding.cjs +3 -3
- package/batteries/tools/encoding.mjs +3 -3
- package/batteries/tools/formatting.cjs +3 -3
- package/batteries/tools/formatting.mjs +3 -3
- package/batteries/tools/geo_basics.cjs +2 -2
- package/batteries/tools/geo_basics.mjs +2 -2
- package/batteries/tools/math/index.d.ts +14 -3
- package/batteries/tools/math.cjs +241 -9
- package/batteries/tools/math.cjs.map +1 -1
- package/batteries/tools/math.mjs +241 -9
- package/batteries/tools/math.mjs.map +1 -1
- package/batteries/tools/memory.cjs +5 -5
- package/batteries/tools/memory.mjs +5 -5
- package/batteries/tools/parsing.cjs +4 -4
- package/batteries/tools/parsing.mjs +4 -4
- package/batteries/tools/retrievables.cjs +4 -4
- package/batteries/tools/retrievables.mjs +4 -4
- package/batteries/tools/standing_instructions.cjs +4 -4
- package/batteries/tools/standing_instructions.mjs +4 -4
- package/batteries/tools/statistics.cjs +4 -4
- package/batteries/tools/statistics.mjs +4 -4
- package/batteries/tools/string_processing.cjs +3 -3
- package/batteries/tools/string_processing.mjs +3 -3
- package/batteries/tools/structured_data.cjs +3 -3
- package/batteries/tools/structured_data.mjs +3 -3
- package/batteries/tools/text_analysis.cjs +3 -3
- package/batteries/tools/text_analysis.mjs +3 -3
- package/batteries/tools/text_comparison.cjs +2 -2
- package/batteries/tools/text_comparison.mjs +2 -2
- package/batteries/tools/time.cjs +2 -2
- package/batteries/tools/time.mjs +2 -2
- package/batteries/tools/unit_conversion.cjs +2 -2
- package/batteries/tools/unit_conversion.mjs +2 -2
- package/{common-GPkVZ_S_.js → common-D_e5zYsG.js} +8 -8
- package/{common-GPkVZ_S_.js.map → common-D_e5zYsG.js.map} +1 -1
- package/{common-DaOXYsz4.mjs → common-lMrnzoyn.mjs} +8 -8
- package/{common-DaOXYsz4.mjs.map → common-lMrnzoyn.mjs.map} +1 -1
- package/common.cjs +7 -7
- package/common.mjs +7 -7
- package/{dispatch_runner-BSJnHGll.mjs → dispatch_runner-CDF3X0nv.mjs} +3 -3
- package/{dispatch_runner-BSJnHGll.mjs.map → dispatch_runner-CDF3X0nv.mjs.map} +1 -1
- package/{dispatch_runner-CwBEskd7.js → dispatch_runner-CpuyATj1.js} +3 -3
- package/{dispatch_runner-CwBEskd7.js.map → dispatch_runner-CpuyATj1.js.map} +1 -1
- package/dispatch_runner.cjs +1 -1
- package/dispatch_runner.mjs +1 -1
- package/exceptions.cjs +1 -1
- package/exceptions.mjs +1 -1
- package/forge.cjs +4 -4
- package/forge.mjs +4 -4
- package/guards.cjs +8 -8
- package/guards.mjs +8 -8
- package/index.cjs +12 -12
- package/index.mjs +12 -12
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +100 -100
- package/{runtime-DQ9CcNdt.js → runtime-MFFcJrRv.js} +1 -1
- package/{runtime-DQ9CcNdt.js.map → runtime-MFFcJrRv.js.map} +1 -1
- package/{runtime-DZ892Rht.mjs → runtime-j92CNi5z.mjs} +1 -1
- package/{runtime-DZ892Rht.mjs.map → runtime-j92CNi5z.mjs.map} +1 -1
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{spooled_artifact-7A9-TJeb.js → spooled_artifact-B8gIIn9h.js} +4 -4
- package/{spooled_artifact-7A9-TJeb.js.map → spooled_artifact-B8gIIn9h.js.map} +1 -1
- package/{spooled_artifact-D_K8dkI5.mjs → spooled_artifact-CWoKUDEm.mjs} +4 -4
- package/{spooled_artifact-D_K8dkI5.mjs.map → spooled_artifact-CWoKUDEm.mjs.map} +1 -1
- package/spooled_artifact.cjs +2 -2
- package/spooled_artifact.mjs +2 -2
- package/{spooled_markdown_artifact-CSMXhajY.mjs → spooled_markdown_artifact-CNle4jXN.mjs} +3 -3
- package/{spooled_markdown_artifact-CSMXhajY.mjs.map → spooled_markdown_artifact-CNle4jXN.mjs.map} +1 -1
- package/{spooled_markdown_artifact-yno0AFt2.js → spooled_markdown_artifact-DQX0RCdI.js} +3 -3
- package/{spooled_markdown_artifact-yno0AFt2.js.map → spooled_markdown_artifact-DQX0RCdI.js.map} +1 -1
- package/{thought-D4Urt3-j.js → thought-BD6AkkOr.js} +4 -4
- package/{thought-D4Urt3-j.js.map → thought-BD6AkkOr.js.map} +1 -1
- package/{thought-C5M4KlGw.mjs → thought-B_P8LiB6.mjs} +4 -4
- package/{thought-C5M4KlGw.mjs.map → thought-B_P8LiB6.mjs.map} +1 -1
- package/{tool-DDqsPmsX.mjs → tool-CRZSUcdP.mjs} +3 -3
- package/{tool-DDqsPmsX.mjs.map → tool-CRZSUcdP.mjs.map} +1 -1
- package/{tool-BwV8Zwpx.js → tool-CX9vNHAw.js} +3 -3
- package/{tool-BwV8Zwpx.js.map → tool-CX9vNHAw.js.map} +1 -1
- package/{tool_call-_5_kLSni.js → tool_call--7ti-frB.js} +4 -4
- package/{tool_call-_5_kLSni.js.map → tool_call--7ti-frB.js.map} +1 -1
- package/{tool_call-DdqI6Lv7.mjs → tool_call-BUeMuCc6.mjs} +4 -4
- package/{tool_call-DdqI6Lv7.mjs.map → tool_call-BUeMuCc6.mjs.map} +1 -1
- package/{tool_registry-CiNgQfJR.mjs → tool_registry-BGHg6KTq.mjs} +3 -3
- package/{tool_registry-CiNgQfJR.mjs.map → tool_registry-BGHg6KTq.mjs.map} +1 -1
- package/{tool_registry-BQVQtiJ5.js → tool_registry-CtCQ4Xoz.js} +3 -3
- package/{tool_registry-BQVQtiJ5.js.map → tool_registry-CtCQ4Xoz.js.map} +1 -1
- package/{turn_runner-B7IQ1s_1.js → turn_runner-BJTtAORU.js} +5 -5
- package/{turn_runner-B7IQ1s_1.js.map → turn_runner-BJTtAORU.js.map} +1 -1
- package/{turn_runner-DbG1qKPG.mjs → turn_runner-C02LZHjt.mjs} +5 -5
- package/{turn_runner-DbG1qKPG.mjs.map → turn_runner-C02LZHjt.mjs.map} +1 -1
- package/turn_runner.cjs +1 -1
- package/turn_runner.mjs +1 -1
package/batteries/tools/math.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../chunk-Ble4zEEl.js");
|
|
3
|
-
const require_tool_registry = require("../../tool_registry-
|
|
4
|
-
const require_tool = require("../../tool-
|
|
5
|
-
require("../../common-
|
|
3
|
+
const require_tool_registry = require("../../tool_registry-CtCQ4Xoz.js");
|
|
4
|
+
const require_tool = require("../../tool-CX9vNHAw.js");
|
|
5
|
+
require("../../common-D_e5zYsG.js");
|
|
6
6
|
require("../../guards.cjs");
|
|
7
7
|
let _nhtio_validation = require("@nhtio/validation");
|
|
8
8
|
let mathjs = require("mathjs");
|
|
@@ -45,7 +45,10 @@ function latexToMathjs(latex) {
|
|
|
45
45
|
}
|
|
46
46
|
expr = expr.replace(/\\sqrt\[([^\]]+)\]\s*\{([^{}]*)\}/g, "nthRoot($2, $1)");
|
|
47
47
|
expr = expr.replace(/\\sqrt\s*\{([^{}]*)\}/g, "sqrt($1)");
|
|
48
|
-
expr = expr.replace(/\\
|
|
48
|
+
expr = expr.replace(/\\arcsin/g, "asin");
|
|
49
|
+
expr = expr.replace(/\\arccos/g, "acos");
|
|
50
|
+
expr = expr.replace(/\\arctan/g, "atan");
|
|
51
|
+
expr = expr.replace(/\\(sin|cos|tan|cot|sec|csc|sinh|cosh|tanh|ln|log|exp|abs|det)/g, "$1");
|
|
49
52
|
expr = expr.replace(/log_\{?(\w+)\}?\s*\(([^)]+)\)/g, "log($2, $1)");
|
|
50
53
|
expr = expr.replace(/\^{([^{}]*)}/g, "^($1)");
|
|
51
54
|
expr = expr.replace(/_\{[^{}]*\}/g, "");
|
|
@@ -70,6 +73,222 @@ function latexToMathjs(latex) {
|
|
|
70
73
|
expr = expr.replace(/\s+/g, " ").trim();
|
|
71
74
|
return expr;
|
|
72
75
|
}
|
|
76
|
+
/** A tagged error whose message is surfaced to the model verbatim (caught by {@link tryCalculus}). */
|
|
77
|
+
var CalculusError = class extends Error {};
|
|
78
|
+
/** Evaluate a mathjs expression at a single point, guarding against non-finite results. */
|
|
79
|
+
function evalAt(expr, varName, x) {
|
|
80
|
+
const y = math.evaluate(expr, { [varName]: x });
|
|
81
|
+
if (typeof y !== "number" || !Number.isFinite(y)) throw new CalculusError("NON_FINITE");
|
|
82
|
+
return y;
|
|
83
|
+
}
|
|
84
|
+
/** Composite Simpson's rule over a finite interval. Throws {@link CalculusError} on a singularity. */
|
|
85
|
+
function simpson(fn, a, b, n = 1e3) {
|
|
86
|
+
if (n % 2 === 1) n++;
|
|
87
|
+
const h = (b - a) / n;
|
|
88
|
+
let sum = fn(a) + fn(b);
|
|
89
|
+
for (let i = 1; i < n; i++) sum += (i % 2 === 1 ? 4 : 2) * fn(a + i * h);
|
|
90
|
+
const result = h / 3 * sum;
|
|
91
|
+
if (!Number.isFinite(result)) throw new CalculusError("NON_FINITE");
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
/** Round numeric output to 12 significant digits, collapsing floating-point noise. */
|
|
95
|
+
function formatNumeric(n) {
|
|
96
|
+
if (Math.abs(n) < 1e-9) return "0";
|
|
97
|
+
return math.format(n, { precision: 12 });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Reads a single `_`/`^` script starting at `i`, supporting braced (`_{0}`, `^{\pi}`), command
|
|
101
|
+
* (`^\pi`), and bare-token (`_0`, `^1`) forms. Returns the mark, its raw-LaTeX value, and the next
|
|
102
|
+
* index, or `null` when there is no script at `i`.
|
|
103
|
+
*/
|
|
104
|
+
function readScript(s, i) {
|
|
105
|
+
while (s[i] === " ") i++;
|
|
106
|
+
const mark = s[i];
|
|
107
|
+
if (mark !== "_" && mark !== "^") return null;
|
|
108
|
+
i++;
|
|
109
|
+
while (s[i] === " ") i++;
|
|
110
|
+
if (s[i] === "{") {
|
|
111
|
+
let depth = 0;
|
|
112
|
+
const start = ++i;
|
|
113
|
+
for (; i < s.length; i++) if (s[i] === "{") depth++;
|
|
114
|
+
else if (s[i] === "}") {
|
|
115
|
+
if (depth === 0) break;
|
|
116
|
+
depth--;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
mark,
|
|
120
|
+
val: s.slice(start, i),
|
|
121
|
+
next: i + 1
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (s[i] === "\\") {
|
|
125
|
+
const m = /^\\[a-zA-Z]+/.exec(s.slice(i));
|
|
126
|
+
if (!m) return null;
|
|
127
|
+
return {
|
|
128
|
+
mark,
|
|
129
|
+
val: m[0],
|
|
130
|
+
next: i + m[0].length
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const m = /^[A-Za-z0-9.]+/.exec(s.slice(i));
|
|
134
|
+
if (!m) return null;
|
|
135
|
+
return {
|
|
136
|
+
mark,
|
|
137
|
+
val: m[0],
|
|
138
|
+
next: i + m[0].length
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Translate a raw-LaTeX bound/target fragment and evaluate it to a finite number. */
|
|
142
|
+
function evalBound(latex) {
|
|
143
|
+
const trimmed = latex.trim();
|
|
144
|
+
if (/\\infty/.test(trimmed)) throw new CalculusError("INFINITE_BOUND");
|
|
145
|
+
const mathjsExpr = latexToMathjs(trimmed);
|
|
146
|
+
const value = math.evaluate(mathjsExpr);
|
|
147
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new CalculusError("BAD_BOUND");
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
/** Evaluate a definite integral `\int_{a}^{b} f \,dx` by Simpson quadrature. */
|
|
151
|
+
function evalIntegral(s) {
|
|
152
|
+
const m = /\\int(?:\\limits)?/.exec(s);
|
|
153
|
+
if (!m) throw new CalculusError("NOT_INTEGRAL");
|
|
154
|
+
let i = m.index + m[0].length;
|
|
155
|
+
const scripts = {
|
|
156
|
+
"_": void 0,
|
|
157
|
+
"^": void 0
|
|
158
|
+
};
|
|
159
|
+
for (let k = 0; k < 2; k++) {
|
|
160
|
+
const sc = readScript(s, i);
|
|
161
|
+
if (!sc) break;
|
|
162
|
+
scripts[sc.mark] = sc.val;
|
|
163
|
+
i = sc.next;
|
|
164
|
+
}
|
|
165
|
+
if (scripts._ === void 0 || scripts["^"] === void 0) throw new CalculusError("INDEFINITE");
|
|
166
|
+
const rest = s.slice(i);
|
|
167
|
+
const diff = /\\?,?\s*\bd\s*([a-zA-Z])\s*$/.exec(rest);
|
|
168
|
+
if (!diff) throw new CalculusError("NO_DIFFERENTIAL");
|
|
169
|
+
const variable = diff[1];
|
|
170
|
+
const integrandLatex = rest.slice(0, diff.index);
|
|
171
|
+
if (integrandLatex.trim().length === 0) throw new CalculusError("NO_INTEGRAND");
|
|
172
|
+
const a = evalBound(scripts._);
|
|
173
|
+
const b = evalBound(scripts["^"]);
|
|
174
|
+
const integrand = latexToMathjs(integrandLatex);
|
|
175
|
+
const fn = (x) => evalAt(integrand, variable, x);
|
|
176
|
+
let result;
|
|
177
|
+
if (a === b) result = 0;
|
|
178
|
+
else if (a < b) result = simpson(fn, a, b);
|
|
179
|
+
else result = -simpson(fn, b, a);
|
|
180
|
+
return `Converted: ∫(${integrand}) d${variable} from ${formatNumeric(a)} to ${formatNumeric(b)}\nResult (numeric): ${formatNumeric(result)}`;
|
|
181
|
+
}
|
|
182
|
+
/** Evaluate a derivative `\frac{d}{dx} f \big|_{x=a}` at a point via central finite difference. */
|
|
183
|
+
function evalDerivative(s) {
|
|
184
|
+
let variable;
|
|
185
|
+
let body = s;
|
|
186
|
+
const fracOp = /\\frac\s*\{\s*d\s*\}\s*\{\s*d\s*([a-zA-Z])\s*\}/.exec(s);
|
|
187
|
+
if (fracOp) {
|
|
188
|
+
variable = fracOp[1];
|
|
189
|
+
body = s.slice(fracOp.index + fracOp[0].length);
|
|
190
|
+
} else {
|
|
191
|
+
const bareOp = /(?:^|[^a-zA-Z])d\s*\/\s*d([a-zA-Z])/.exec(s);
|
|
192
|
+
if (bareOp) {
|
|
193
|
+
variable = bareOp[1];
|
|
194
|
+
body = s.slice(bareOp.index + bareOp[0].length);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!variable) throw new CalculusError("NOT_DERIVATIVE");
|
|
198
|
+
body = body.replace(/\\left\.?/g, "").replace(/\\right\.?/g, "");
|
|
199
|
+
const bar = /\\?(?:Big|big|bigg|Bigg)?\s*\|\s*_\s*\{?\s*([a-zA-Z])\s*=\s*([^}]+?)\s*\}?\s*$/.exec(body);
|
|
200
|
+
if (!bar) throw new CalculusError("NO_POINT");
|
|
201
|
+
const point = evalBound(bar[2]);
|
|
202
|
+
let fnLatex = body.slice(0, bar.index).trim();
|
|
203
|
+
fnLatex = fnLatex.replace(/^\\?\(([\s\S]*)\\?\)$/, "$1").replace(/^\[([\s\S]*)\]$/, "$1").trim();
|
|
204
|
+
if (fnLatex.length === 0) throw new CalculusError("NO_FUNCTION");
|
|
205
|
+
const expr = latexToMathjs(fnLatex);
|
|
206
|
+
const h = 1e-6;
|
|
207
|
+
const fn = (x) => evalAt(expr, variable, x);
|
|
208
|
+
const result = (fn(point + h) - fn(point - h)) / (2 * h);
|
|
209
|
+
if (!Number.isFinite(result)) throw new CalculusError("NON_FINITE");
|
|
210
|
+
return `Converted: d/d${variable}(${expr}) at ${variable}=${formatNumeric(point)}\nResult (numeric): ${formatNumeric(result)}`;
|
|
211
|
+
}
|
|
212
|
+
/** Evaluate a limit `\lim_{x \to a} f(x)` by a two-sided numeric approach. */
|
|
213
|
+
function evalLimit(s) {
|
|
214
|
+
const head = /\\lim\s*_\s*\{\s*([a-zA-Z])\s*\\to\s*([\s\S]+?)\s*\}/.exec(s);
|
|
215
|
+
if (!head) throw new CalculusError("NOT_LIMIT");
|
|
216
|
+
const variable = head[1];
|
|
217
|
+
let targetLatex = head[2].trim();
|
|
218
|
+
const bodyLatex = s.slice(head.index + head[0].length).trim();
|
|
219
|
+
if (bodyLatex.length === 0) throw new CalculusError("NO_FUNCTION");
|
|
220
|
+
let side = "both";
|
|
221
|
+
const oneSided = /\^\s*\{?\s*([+-])\s*\}?\s*$/.exec(targetLatex);
|
|
222
|
+
if (oneSided) {
|
|
223
|
+
side = oneSided[1] === "+" ? "plus" : "minus";
|
|
224
|
+
targetLatex = targetLatex.slice(0, oneSided.index).trim();
|
|
225
|
+
}
|
|
226
|
+
const expr = latexToMathjs(bodyLatex);
|
|
227
|
+
const fn = (x) => evalAt(expr, variable, x);
|
|
228
|
+
const eps = 1e-6;
|
|
229
|
+
let result;
|
|
230
|
+
let targetLabel;
|
|
231
|
+
if (/^-\s*\\infty$/.test(targetLatex)) {
|
|
232
|
+
result = fn(-1e8);
|
|
233
|
+
targetLabel = "-∞";
|
|
234
|
+
} else if (/^\\infty$/.test(targetLatex)) {
|
|
235
|
+
result = fn(1e8);
|
|
236
|
+
targetLabel = "∞";
|
|
237
|
+
} else {
|
|
238
|
+
const target = evalBound(targetLatex);
|
|
239
|
+
targetLabel = formatNumeric(target);
|
|
240
|
+
if (side === "plus") result = fn(target + eps);
|
|
241
|
+
else if (side === "minus") result = fn(target - eps);
|
|
242
|
+
else {
|
|
243
|
+
const lo = fn(target - eps);
|
|
244
|
+
const hi = fn(target + eps);
|
|
245
|
+
const avg = (lo + hi) / 2;
|
|
246
|
+
if (Math.abs(hi - lo) > .001 * Math.max(1, Math.abs(avg))) throw new CalculusError("LIMIT_MISMATCH");
|
|
247
|
+
result = avg;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!Number.isFinite(result)) throw new CalculusError("LIMIT_UNBOUNDED");
|
|
251
|
+
const nearest = Math.round(result);
|
|
252
|
+
if (Math.abs(result - nearest) < 1e-6) result = nearest;
|
|
253
|
+
return `Converted: lim ${variable}→${targetLabel} of (${expr})\nResult (numeric): ${formatNumeric(result)}`;
|
|
254
|
+
}
|
|
255
|
+
/** Human-readable, model-actionable message for each {@link CalculusError} tag. */
|
|
256
|
+
function calculusErrorMessage(tag) {
|
|
257
|
+
switch (tag) {
|
|
258
|
+
case "INDEFINITE": return "Cannot evaluate an indefinite integral numerically. Provide bounds, e.g. \\int_{0}^{1} x dx.";
|
|
259
|
+
case "NO_DIFFERENTIAL": return "Could not find the integration variable. Expected a trailing differential like 'dx'.";
|
|
260
|
+
case "NO_INTEGRAND": return "The integral has no integrand to evaluate.";
|
|
261
|
+
case "INFINITE_BOUND": return "Infinite integration bounds are not supported. Provide finite numeric bounds, e.g. \\int_{0}^{1} f dx.";
|
|
262
|
+
case "BAD_BOUND": return "Could not evaluate the integration bounds to numbers.";
|
|
263
|
+
case "NO_POINT": return "Cannot evaluate a derivative numerically without a point. Specify where, e.g. \\frac{d}{dx}(x^2)\\Big|_{x=3}.";
|
|
264
|
+
case "NO_FUNCTION": return "Could not find the function to evaluate.";
|
|
265
|
+
case "LIMIT_MISMATCH": return "The limit may not exist (left and right values disagree).";
|
|
266
|
+
case "LIMIT_UNBOUNDED": return "The limit appears to diverge (function is unbounded near the target).";
|
|
267
|
+
case "NON_FINITE": return "The expression is not finite over the requested range (possible singularity); cannot evaluate numerically.";
|
|
268
|
+
default: return "Could not evaluate the calculus expression.";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Detects a calculus construct in raw LaTeX and routes it to the matching numeric handler. Returns
|
|
273
|
+
* the result/error string, or `null` when the input is not a calculus expression (so the caller
|
|
274
|
+
* falls through to the scalar evaluation path unchanged).
|
|
275
|
+
*/
|
|
276
|
+
function tryCalculus(latex) {
|
|
277
|
+
let s = latex.trim();
|
|
278
|
+
s = s.replace(/^\$\$?|\$\$?$/g, "");
|
|
279
|
+
s = s.replace(/^\\[[(]|\\[\])]$/g, "").trim();
|
|
280
|
+
let handler;
|
|
281
|
+
if (/\\int/.test(s)) handler = evalIntegral;
|
|
282
|
+
else if (/\\lim/.test(s)) handler = evalLimit;
|
|
283
|
+
else if (/\\frac\s*\{\s*d\s*\}\s*\{\s*d\s*[a-zA-Z]\s*\}/.test(s) || /(?:^|[^a-zA-Z])d\s*\/\s*d[a-zA-Z]/.test(s)) handler = evalDerivative;
|
|
284
|
+
if (!handler) return null;
|
|
285
|
+
try {
|
|
286
|
+
return handler(s);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (require_tool_registry.isInstanceOf(err, "CalculusError", CalculusError)) return `Error: ${calculusErrorMessage(err.message)}`;
|
|
289
|
+
return `Error: ${require_tool_registry.isError(err) ? err.message : String(err)}`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
73
292
|
/**
|
|
74
293
|
* Evaluates a mathjs-syntax expression and returns the numeric result alongside the KaTeX
|
|
75
294
|
* representation of the parsed expression.
|
|
@@ -105,21 +324,34 @@ var calculateTool = new require_tool.Tool({
|
|
|
105
324
|
*
|
|
106
325
|
* @remarks
|
|
107
326
|
* Translates common LaTeX constructs (`\frac{a}{b}`, `\sqrt{...}`, `\cdot`, `\times`, Greek
|
|
108
|
-
* macros like `\pi`, `\left`/`\right` delimiters,
|
|
109
|
-
* mathjs equivalents before evaluation. Both the source
|
|
110
|
-
* are subject to the 1000-character length cap.
|
|
327
|
+
* macros like `\pi`, inverse trig `\arcsin`/`\arccos`/`\arctan`, `\left`/`\right` delimiters,
|
|
328
|
+
* `\text{...}`, subscripts, etc.) into their mathjs equivalents before evaluation. Both the source
|
|
329
|
+
* and the translated mathjs expression are subject to the 1000-character length cap.
|
|
330
|
+
*
|
|
331
|
+
* Also evaluates three calculus constructs **numerically** (mathjs has no symbolic integration, and
|
|
332
|
+
* its symbolic `derivative` is blocklisted here for safety):
|
|
333
|
+
* - Definite integrals — `\int_{a}^{b} f \,dx` via composite Simpson quadrature.
|
|
334
|
+
* - Derivatives at a point — `\frac{d}{dx} f \big|_{x=a}` via central finite difference.
|
|
335
|
+
* - Limits — `\lim_{x \to a} f` (including `a = \pm\infty`) via a two-sided numeric approach.
|
|
336
|
+
*
|
|
337
|
+
* Numeric results are rounded with `math.format(..., { precision: 12 })` and labelled
|
|
338
|
+
* `Result (numeric):` to flag that they are approximations. Constructs that cannot be evaluated
|
|
339
|
+
* numerically (indefinite integrals, derivatives without a point, infinite integration bounds,
|
|
340
|
+
* singular integrands, divergent limits) return a specific, guiding error string.
|
|
111
341
|
*
|
|
112
342
|
* Parse and evaluation errors are returned as error strings rather than thrown.
|
|
113
343
|
*/
|
|
114
344
|
var evaluateKatexTool = new require_tool.Tool({
|
|
115
345
|
name: "evaluate_katex",
|
|
116
|
-
description: "Evaluate a KaTeX/LaTeX math expression and return the numeric result.",
|
|
117
|
-
inputSchema: _nhtio_validation.validator.object({ katex: _nhtio_validation.validator.string().required().description("LaTeX expression, e.g. \"\\frac{1}{2} + \\sqrt{9}\"") }),
|
|
346
|
+
description: "Evaluate a KaTeX/LaTeX math expression and return the numeric result. Supports arithmetic, trig, logs, roots, and numeric calculus: definite integrals (\\int_{a}^{b} f dx), derivatives at a point (\\frac{d}{dx} f|_{x=a}), and limits (\\lim_{x \\to a} f).",
|
|
347
|
+
inputSchema: _nhtio_validation.validator.object({ katex: _nhtio_validation.validator.string().required().description("LaTeX expression, e.g. \"\\frac{1}{2} + \\sqrt{9}\" or \"\\int_{0}^{1} x^2 dx\"") }),
|
|
118
348
|
handler: async (args) => {
|
|
119
349
|
const { katex } = args;
|
|
120
350
|
const lengthError = validateExpression(katex);
|
|
121
351
|
if (lengthError) return lengthError;
|
|
122
352
|
try {
|
|
353
|
+
const calculus = tryCalculus(katex);
|
|
354
|
+
if (calculus !== null) return calculus;
|
|
123
355
|
const mathjsExpr = latexToMathjs(katex);
|
|
124
356
|
const translatedLengthError = validateExpression(mathjsExpr);
|
|
125
357
|
if (translatedLengthError) return translatedLengthError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math.cjs","names":[],"sources":["../../../src/batteries/tools/math/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for safe arithmetic, expression evaluation, and numeric operations.\n *\n * @module @nhtio/adk/batteries/tools/math\n *\n * @remarks\n * Pre-constructed bundled tools for the `math` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { create, all } from 'mathjs'\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\nconst math = create(all)\n\nconst BLOCKED_FUNCTIONS = [\n 'import',\n 'createUnit',\n 'simplify',\n 'derivative',\n 'compile',\n 'chain',\n 'reviver',\n 'replacer',\n]\n\nfor (const fn of BLOCKED_FUNCTIONS) {\n if (fn in math) {\n ;(math as any)[fn] = undefined\n }\n}\n\nconst MAX_EXPRESSION_LENGTH = 1000\n\nfunction validateExpression(expr: string): string | undefined {\n if (expr.length > MAX_EXPRESSION_LENGTH) {\n return `Expression too long (max ${MAX_EXPRESSION_LENGTH} characters).`\n }\n return undefined\n}\n\n/**\n * Convert common LaTeX/KaTeX notation to a mathjs-evaluable expression.\n */\nfunction latexToMathjs(latex: string): string {\n let expr = latex.trim()\n\n expr = expr.replace(/^\\$\\$?|\\$\\$?$/g, '')\n expr = expr.replace(/^\\\\[[(]|\\\\[\\])]$/g, '')\n\n for (let i = 0; i < 10; i++) {\n const before = expr\n expr = expr.replace(/\\\\frac\\s*\\{([^{}]*)\\}\\s*\\{([^{}]*)\\}/g, '(($1)/($2))')\n if (expr === before) break\n }\n\n expr = expr.replace(/\\\\sqrt\\[([^\\]]+)\\]\\s*\\{([^{}]*)\\}/g, 'nthRoot($2, $1)')\n expr = expr.replace(/\\\\sqrt\\s*\\{([^{}]*)\\}/g, 'sqrt($1)')\n\n expr = expr.replace(\n /\\\\(sin|cos|tan|cot|sec|csc|arcsin|arccos|arctan|sinh|cosh|tanh|ln|log|exp|abs|det)/g,\n '$1'\n )\n\n expr = expr.replace(/log_\\{?(\\w+)\\}?\\s*\\(([^)]+)\\)/g, 'log($2, $1)')\n expr = expr.replace(/\\^{([^{}]*)}/g, '^($1)')\n expr = expr.replace(/_\\{[^{}]*\\}/g, '')\n expr = expr.replace(/_\\w/g, '')\n\n expr = expr.replace(/\\\\pi/g, 'pi')\n expr = expr.replace(\n /\\\\(alpha|beta|gamma|delta|epsilon|theta|lambda|mu|sigma|tau|phi|omega)/g,\n '$1'\n )\n\n expr = expr.replace(/\\\\cdot/g, '*')\n expr = expr.replace(/\\\\times/g, '*')\n expr = expr.replace(/\\\\div/g, '/')\n expr = expr.replace(/\\\\infty/g, 'Infinity')\n expr = expr.replace(/\\\\left\\s*([([{|])/g, '$1')\n expr = expr.replace(/\\\\right\\s*([)\\]}|])/g, '$1')\n expr = expr.replace(/\\\\sum/g, 'sum')\n expr = expr.replace(/\\\\prod/g, 'prod')\n expr = expr.replace(/\\\\,/g, ' ')\n expr = expr.replace(/\\\\;/g, ' ')\n expr = expr.replace(/\\\\quad/g, ' ')\n expr = expr.replace(/\\\\qquad/g, ' ')\n expr = expr.replace(/\\\\text\\{([^{}]*)\\}/g, '')\n expr = expr.replace(/(\\d)([a-zA-Z(])/g, '$1*$2')\n expr = expr.replace(/\\)\\s*\\(/g, ')*(')\n expr = expr.replace(/\\s+/g, ' ').trim()\n\n return expr\n}\n\n/**\n * Evaluates a mathjs-syntax expression and returns the numeric result alongside the KaTeX\n * representation of the parsed expression.\n *\n * @remarks\n * Supports arithmetic, trigonometric, logarithmic, exponential, factorial, matrix, and unit\n * operations via `mathjs`. The mathjs instance is hardened: dangerous functions (`import`,\n * `createUnit`, `simplify`, `derivative`, `compile`, `chain`, `reviver`, `replacer`) are\n * disabled to prevent interpreter-surface exposure.\n *\n * Expressions over 1000 characters are rejected with an error string (not thrown). Parse and\n * evaluation errors are also returned as error strings — the tool surfaces math errors as\n * content rather than exceptions, so the model can react to them in-line.\n */\nexport const calculateTool = new Tool({\n name: 'calculate',\n description:\n 'Evaluate a math expression. Supports arithmetic, trig, log, sqrt, factorial, matrices.',\n inputSchema: validator.object({\n expression: validator.string().required().description('Math expression, e.g. \"sin(pi/4) + 5!\"'),\n }),\n handler: async (args) => {\n const { expression } = args as { expression: string }\n\n const lengthError = validateExpression(expression)\n if (lengthError) return lengthError\n\n try {\n const result = math.evaluate!(expression)\n const node = math.parse!(expression)\n const katex = node.toTex()\n return `Result: ${result}\\nKaTeX: $${katex} = ${result}$`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Translates a LaTeX/KaTeX expression into mathjs syntax, evaluates it, and returns the result.\n *\n * @remarks\n * Translates common LaTeX constructs (`\\frac{a}{b}`, `\\sqrt{...}`, `\\cdot`, `\\times`, Greek\n * macros like `\\pi`, `\\left`/`\\right` delimiters, `\\text{...}`, subscripts, etc.) into their\n * mathjs equivalents before evaluation. Both the source and the translated mathjs expression\n * are subject to the 1000-character length cap.\n *\n * Parse and evaluation errors are returned as error strings rather than thrown.\n */\nexport const evaluateKatexTool = new Tool({\n name: 'evaluate_katex',\n description: 'Evaluate a KaTeX/LaTeX math expression and return the numeric result.',\n inputSchema: validator.object({\n katex: validator\n .string()\n .required()\n .description('LaTeX expression, e.g. \"\\\\frac{1}{2} + \\\\sqrt{9}\"'),\n }),\n handler: async (args) => {\n const { katex } = args as { katex: string }\n\n const lengthError = validateExpression(katex)\n if (lengthError) return lengthError\n\n try {\n const mathjsExpr = latexToMathjs(katex)\n\n const translatedLengthError = validateExpression(mathjsExpr)\n if (translatedLengthError) return translatedLengthError\n\n const result = math.evaluate!(mathjsExpr)\n return `Converted: ${mathjsExpr}\\nResult: ${result}`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"math.cjs","names":[],"sources":["../../../src/batteries/tools/math/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for safe arithmetic, expression evaluation, and numeric operations.\n *\n * @module @nhtio/adk/batteries/tools/math\n *\n * @remarks\n * Pre-constructed bundled tools for the `math` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { create, all } from 'mathjs'\nimport { Tool } from '@nhtio/adk/common'\nimport { validator } from '@nhtio/validation'\nimport { isError, isInstanceOf } from '@nhtio/adk/guards'\n\nconst math = create(all)\n\nconst BLOCKED_FUNCTIONS = [\n 'import',\n 'createUnit',\n 'simplify',\n 'derivative',\n 'compile',\n 'chain',\n 'reviver',\n 'replacer',\n]\n\nfor (const fn of BLOCKED_FUNCTIONS) {\n if (fn in math) {\n ;(math as any)[fn] = undefined\n }\n}\n\nconst MAX_EXPRESSION_LENGTH = 1000\n\nfunction validateExpression(expr: string): string | undefined {\n if (expr.length > MAX_EXPRESSION_LENGTH) {\n return `Expression too long (max ${MAX_EXPRESSION_LENGTH} characters).`\n }\n return undefined\n}\n\n/**\n * Convert common LaTeX/KaTeX notation to a mathjs-evaluable expression.\n */\nfunction latexToMathjs(latex: string): string {\n let expr = latex.trim()\n\n expr = expr.replace(/^\\$\\$?|\\$\\$?$/g, '')\n expr = expr.replace(/^\\\\[[(]|\\\\[\\])]$/g, '')\n\n for (let i = 0; i < 10; i++) {\n const before = expr\n expr = expr.replace(/\\\\frac\\s*\\{([^{}]*)\\}\\s*\\{([^{}]*)\\}/g, '(($1)/($2))')\n if (expr === before) break\n }\n\n expr = expr.replace(/\\\\sqrt\\[([^\\]]+)\\]\\s*\\{([^{}]*)\\}/g, 'nthRoot($2, $1)')\n expr = expr.replace(/\\\\sqrt\\s*\\{([^{}]*)\\}/g, 'sqrt($1)')\n\n // Inverse trig: mathjs spells these asin/acos/atan, not arcsin/arccos/arctan.\n expr = expr.replace(/\\\\arcsin/g, 'asin')\n expr = expr.replace(/\\\\arccos/g, 'acos')\n expr = expr.replace(/\\\\arctan/g, 'atan')\n\n expr = expr.replace(/\\\\(sin|cos|tan|cot|sec|csc|sinh|cosh|tanh|ln|log|exp|abs|det)/g, '$1')\n\n expr = expr.replace(/log_\\{?(\\w+)\\}?\\s*\\(([^)]+)\\)/g, 'log($2, $1)')\n expr = expr.replace(/\\^{([^{}]*)}/g, '^($1)')\n expr = expr.replace(/_\\{[^{}]*\\}/g, '')\n expr = expr.replace(/_\\w/g, '')\n\n expr = expr.replace(/\\\\pi/g, 'pi')\n expr = expr.replace(\n /\\\\(alpha|beta|gamma|delta|epsilon|theta|lambda|mu|sigma|tau|phi|omega)/g,\n '$1'\n )\n\n expr = expr.replace(/\\\\cdot/g, '*')\n expr = expr.replace(/\\\\times/g, '*')\n expr = expr.replace(/\\\\div/g, '/')\n expr = expr.replace(/\\\\infty/g, 'Infinity')\n expr = expr.replace(/\\\\left\\s*([([{|])/g, '$1')\n expr = expr.replace(/\\\\right\\s*([)\\]}|])/g, '$1')\n expr = expr.replace(/\\\\sum/g, 'sum')\n expr = expr.replace(/\\\\prod/g, 'prod')\n expr = expr.replace(/\\\\,/g, ' ')\n expr = expr.replace(/\\\\;/g, ' ')\n expr = expr.replace(/\\\\quad/g, ' ')\n expr = expr.replace(/\\\\qquad/g, ' ')\n expr = expr.replace(/\\\\text\\{([^{}]*)\\}/g, '')\n expr = expr.replace(/(\\d)([a-zA-Z(])/g, '$1*$2')\n expr = expr.replace(/\\)\\s*\\(/g, ')*(')\n expr = expr.replace(/\\s+/g, ' ').trim()\n\n return expr\n}\n\n// ─── Numeric calculus ─────────────────────────────────────────────────────────\n//\n// `evaluate_katex` is a numeric evaluator. mathjs has no symbolic integration, and its symbolic\n// `derivative` is intentionally blocklisted above, so calculus is computed NUMERICALLY: definite\n// integrals via composite Simpson quadrature, derivatives via central finite differences, and limits\n// via a two-sided approach. These are approximations (correct to ~1e-9 for the smooth expressions a\n// model emits), surfaced under a `Result (numeric):` label so the distinction is explicit.\n//\n// Detection runs on the RAW LaTeX, before `latexToMathjs` flattens it — its subscript stripping\n// would otherwise destroy integral bounds (`\\int_{0}^{1}` → `\\int^(1)`) and limit targets. The\n// extracted sub-expressions (integrand, bounds, body, point) are then translated by the existing\n// `latexToMathjs` and evaluated per-point with a scope, which works under the security blocklist.\n\n/** A tagged error whose message is surfaced to the model verbatim (caught by {@link tryCalculus}). */\nclass CalculusError extends Error {}\n\n/** Evaluate a mathjs expression at a single point, guarding against non-finite results. */\nfunction evalAt(expr: string, varName: string, x: number): number {\n const y = math.evaluate!(expr, { [varName]: x })\n if (typeof y !== 'number' || !Number.isFinite(y)) {\n throw new CalculusError('NON_FINITE')\n }\n return y\n}\n\n/** Composite Simpson's rule over a finite interval. Throws {@link CalculusError} on a singularity. */\nfunction simpson(fn: (x: number) => number, a: number, b: number, n = 1000): number {\n if (n % 2 === 1) n++\n const h = (b - a) / n\n let sum = fn(a) + fn(b)\n for (let i = 1; i < n; i++) {\n sum += (i % 2 === 1 ? 4 : 2) * fn(a + i * h)\n }\n const result = (h / 3) * sum\n if (!Number.isFinite(result)) throw new CalculusError('NON_FINITE')\n return result\n}\n\n/** Round numeric output to 12 significant digits, collapsing floating-point noise. */\nfunction formatNumeric(n: number): string {\n // Snap values that are zero-to-within-tolerance to exactly 0 (e.g. lim_{x->inf} 1/x ≈ 1e-8).\n if (Math.abs(n) < 1e-9) return '0'\n return math.format!(n, { precision: 12 })\n}\n\n/**\n * Reads a single `_`/`^` script starting at `i`, supporting braced (`_{0}`, `^{\\pi}`), command\n * (`^\\pi`), and bare-token (`_0`, `^1`) forms. Returns the mark, its raw-LaTeX value, and the next\n * index, or `null` when there is no script at `i`.\n */\nfunction readScript(s: string, i: number): { mark: '_' | '^'; val: string; next: number } | null {\n while (s[i] === ' ') i++\n const mark = s[i]\n if (mark !== '_' && mark !== '^') return null\n i++\n while (s[i] === ' ') i++\n if (s[i] === '{') {\n let depth = 0\n const start = ++i\n for (; i < s.length; i++) {\n if (s[i] === '{') depth++\n else if (s[i] === '}') {\n if (depth === 0) break\n depth--\n }\n }\n return { mark, val: s.slice(start, i), next: i + 1 }\n }\n if (s[i] === '\\\\') {\n const m = /^\\\\[a-zA-Z]+/.exec(s.slice(i))\n if (!m) return null\n return { mark, val: m[0], next: i + m[0].length }\n }\n const m = /^[A-Za-z0-9.]+/.exec(s.slice(i))\n if (!m) return null\n return { mark, val: m[0], next: i + m[0].length }\n}\n\n/** Translate a raw-LaTeX bound/target fragment and evaluate it to a finite number. */\nfunction evalBound(latex: string): number {\n const trimmed = latex.trim()\n if (/\\\\infty/.test(trimmed)) throw new CalculusError('INFINITE_BOUND')\n const mathjsExpr = latexToMathjs(trimmed)\n const value = math.evaluate!(mathjsExpr)\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new CalculusError('BAD_BOUND')\n }\n return value\n}\n\n/** Evaluate a definite integral `\\int_{a}^{b} f \\,dx` by Simpson quadrature. */\nfunction evalIntegral(s: string): string {\n // Strip the operator (\\int, optionally with \\limits).\n const m = /\\\\int(?:\\\\limits)?/.exec(s)\n if (!m) throw new CalculusError('NOT_INTEGRAL')\n let i = m.index + m[0].length\n\n // Read up to two scripts (bounds), in either order.\n const scripts: Record<'_' | '^', string | undefined> = { '_': undefined, '^': undefined }\n for (let k = 0; k < 2; k++) {\n const sc = readScript(s, i)\n if (!sc) break\n scripts[sc.mark] = sc.val\n i = sc.next\n }\n if (scripts._ === undefined || scripts['^'] === undefined) {\n throw new CalculusError('INDEFINITE')\n }\n\n // The remainder is `integrand … d<var>`. Pull the trailing differential off the end.\n const rest = s.slice(i)\n const diff = /\\\\?,?\\s*\\bd\\s*([a-zA-Z])\\s*$/.exec(rest)\n if (!diff) throw new CalculusError('NO_DIFFERENTIAL')\n const variable = diff[1]\n const integrandLatex = rest.slice(0, diff.index)\n if (integrandLatex.trim().length === 0) throw new CalculusError('NO_INTEGRAND')\n\n const a = evalBound(scripts._)\n const b = evalBound(scripts['^'])\n const integrand = latexToMathjs(integrandLatex)\n const fn = (x: number) => evalAt(integrand, variable, x)\n\n let result: number\n if (a === b) result = 0\n else if (a < b) result = simpson(fn, a, b)\n else result = -simpson(fn, b, a)\n\n return `Converted: ∫(${integrand}) d${variable} from ${formatNumeric(a)} to ${formatNumeric(b)}\\nResult (numeric): ${formatNumeric(result)}`\n}\n\n/** Evaluate a derivative `\\frac{d}{dx} f \\big|_{x=a}` at a point via central finite difference. */\nfunction evalDerivative(s: string): string {\n // Operator + variable: \\frac{d}{dx} … or bare d/dx ….\n let variable: string | undefined\n let body = s\n const fracOp = /\\\\frac\\s*\\{\\s*d\\s*\\}\\s*\\{\\s*d\\s*([a-zA-Z])\\s*\\}/.exec(s)\n if (fracOp) {\n variable = fracOp[1]\n body = s.slice(fracOp.index + fracOp[0].length)\n } else {\n const bareOp = /(?:^|[^a-zA-Z])d\\s*\\/\\s*d([a-zA-Z])/.exec(s)\n if (bareOp) {\n variable = bareOp[1]\n body = s.slice(bareOp.index + bareOp[0].length)\n }\n }\n if (!variable) throw new CalculusError('NOT_DERIVATIVE')\n\n // Remove pure \\left. / \\right. delimiters.\n body = body.replace(/\\\\left\\.?/g, '').replace(/\\\\right\\.?/g, '')\n\n // Evaluation bar + point at the end: …|_{x=3} (optionally \\Big| etc.).\n const bar = /\\\\?(?:Big|big|bigg|Bigg)?\\s*\\|\\s*_\\s*\\{?\\s*([a-zA-Z])\\s*=\\s*([^}]+?)\\s*\\}?\\s*$/.exec(\n body\n )\n if (!bar) throw new CalculusError('NO_POINT')\n const point = evalBound(bar[2])\n let fnLatex = body.slice(0, bar.index).trim()\n\n // Strip one layer of wrapping parentheses/brackets around the function body.\n fnLatex = fnLatex\n .replace(/^\\\\?\\(([\\s\\S]*)\\\\?\\)$/, '$1')\n .replace(/^\\[([\\s\\S]*)\\]$/, '$1')\n .trim()\n if (fnLatex.length === 0) throw new CalculusError('NO_FUNCTION')\n\n const expr = latexToMathjs(fnLatex)\n const h = 1e-6\n const fn = (x: number) => evalAt(expr, variable, x)\n const result = (fn(point + h) - fn(point - h)) / (2 * h)\n if (!Number.isFinite(result)) throw new CalculusError('NON_FINITE')\n\n return `Converted: d/d${variable}(${expr}) at ${variable}=${formatNumeric(point)}\\nResult (numeric): ${formatNumeric(result)}`\n}\n\n/** Evaluate a limit `\\lim_{x \\to a} f(x)` by a two-sided numeric approach. */\nfunction evalLimit(s: string): string {\n const head = /\\\\lim\\s*_\\s*\\{\\s*([a-zA-Z])\\s*\\\\to\\s*([\\s\\S]+?)\\s*\\}/.exec(s)\n if (!head) throw new CalculusError('NOT_LIMIT')\n const variable = head[1]\n let targetLatex = head[2].trim()\n const bodyLatex = s.slice(head.index + head[0].length).trim()\n if (bodyLatex.length === 0) throw new CalculusError('NO_FUNCTION')\n\n // One-sided markers (0^+, 0^-, 0^{+}) — record the side, strip the marker.\n let side: 'both' | 'plus' | 'minus' = 'both'\n const oneSided = /\\^\\s*\\{?\\s*([+-])\\s*\\}?\\s*$/.exec(targetLatex)\n if (oneSided) {\n side = oneSided[1] === '+' ? 'plus' : 'minus'\n targetLatex = targetLatex.slice(0, oneSided.index).trim()\n }\n\n const expr = latexToMathjs(bodyLatex)\n const fn = (x: number) => evalAt(expr, variable, x)\n const eps = 1e-6\n\n // Infinite targets via large-magnitude substitution.\n let result: number\n let targetLabel: string\n if (/^-\\s*\\\\infty$/.test(targetLatex)) {\n result = fn(-1e8)\n targetLabel = '-∞'\n } else if (/^\\\\infty$/.test(targetLatex)) {\n result = fn(1e8)\n targetLabel = '∞'\n } else {\n const target = evalBound(targetLatex)\n targetLabel = formatNumeric(target)\n if (side === 'plus') {\n result = fn(target + eps)\n } else if (side === 'minus') {\n result = fn(target - eps)\n } else {\n const lo = fn(target - eps)\n const hi = fn(target + eps)\n const avg = (lo + hi) / 2\n if (Math.abs(hi - lo) > 1e-3 * Math.max(1, Math.abs(avg))) {\n throw new CalculusError('LIMIT_MISMATCH')\n }\n result = avg\n }\n }\n if (!Number.isFinite(result)) throw new CalculusError('LIMIT_UNBOUNDED')\n\n // Infinite-target substitution (±1e8) and the eps offset leave more residue than Simpson or the\n // derivative difference, so snap a limit result to a nearby round value before formatting:\n // 0.99999998 → 1, 1e-8 → 0. The tolerance is loose enough to absorb the substitution error yet\n // far tighter than the divergence check that already rejected genuinely non-convergent limits.\n const nearest = Math.round(result)\n if (Math.abs(result - nearest) < 1e-6) result = nearest\n\n return `Converted: lim ${variable}→${targetLabel} of (${expr})\\nResult (numeric): ${formatNumeric(result)}`\n}\n\n/** Human-readable, model-actionable message for each {@link CalculusError} tag. */\nfunction calculusErrorMessage(tag: string): string {\n switch (tag) {\n case 'INDEFINITE':\n return 'Cannot evaluate an indefinite integral numerically. Provide bounds, e.g. \\\\int_{0}^{1} x dx.'\n case 'NO_DIFFERENTIAL':\n return \"Could not find the integration variable. Expected a trailing differential like 'dx'.\"\n case 'NO_INTEGRAND':\n return 'The integral has no integrand to evaluate.'\n case 'INFINITE_BOUND':\n return 'Infinite integration bounds are not supported. Provide finite numeric bounds, e.g. \\\\int_{0}^{1} f dx.'\n case 'BAD_BOUND':\n return 'Could not evaluate the integration bounds to numbers.'\n case 'NO_POINT':\n return 'Cannot evaluate a derivative numerically without a point. Specify where, e.g. \\\\frac{d}{dx}(x^2)\\\\Big|_{x=3}.'\n case 'NO_FUNCTION':\n return 'Could not find the function to evaluate.'\n case 'LIMIT_MISMATCH':\n return 'The limit may not exist (left and right values disagree).'\n case 'LIMIT_UNBOUNDED':\n return 'The limit appears to diverge (function is unbounded near the target).'\n case 'NON_FINITE':\n return 'The expression is not finite over the requested range (possible singularity); cannot evaluate numerically.'\n default:\n return 'Could not evaluate the calculus expression.'\n }\n}\n\n/**\n * Detects a calculus construct in raw LaTeX and routes it to the matching numeric handler. Returns\n * the result/error string, or `null` when the input is not a calculus expression (so the caller\n * falls through to the scalar evaluation path unchanged).\n */\nfunction tryCalculus(latex: string): string | null {\n let s = latex.trim()\n s = s.replace(/^\\$\\$?|\\$\\$?$/g, '')\n s = s.replace(/^\\\\[[(]|\\\\[\\])]$/g, '').trim()\n\n let handler: ((expr: string) => string) | undefined\n if (/\\\\int/.test(s)) handler = evalIntegral\n else if (/\\\\lim/.test(s)) handler = evalLimit\n else if (\n /\\\\frac\\s*\\{\\s*d\\s*\\}\\s*\\{\\s*d\\s*[a-zA-Z]\\s*\\}/.test(s) ||\n /(?:^|[^a-zA-Z])d\\s*\\/\\s*d[a-zA-Z]/.test(s)\n )\n handler = evalDerivative\n if (!handler) return null\n\n try {\n return handler(s)\n } catch (err) {\n if (isInstanceOf(err, 'CalculusError', CalculusError)) {\n return `Error: ${calculusErrorMessage(err.message)}`\n }\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n}\n\n/**\n * Evaluates a mathjs-syntax expression and returns the numeric result alongside the KaTeX\n * representation of the parsed expression.\n *\n * @remarks\n * Supports arithmetic, trigonometric, logarithmic, exponential, factorial, matrix, and unit\n * operations via `mathjs`. The mathjs instance is hardened: dangerous functions (`import`,\n * `createUnit`, `simplify`, `derivative`, `compile`, `chain`, `reviver`, `replacer`) are\n * disabled to prevent interpreter-surface exposure.\n *\n * Expressions over 1000 characters are rejected with an error string (not thrown). Parse and\n * evaluation errors are also returned as error strings — the tool surfaces math errors as\n * content rather than exceptions, so the model can react to them in-line.\n */\nexport const calculateTool = new Tool({\n name: 'calculate',\n description:\n 'Evaluate a math expression. Supports arithmetic, trig, log, sqrt, factorial, matrices.',\n inputSchema: validator.object({\n expression: validator.string().required().description('Math expression, e.g. \"sin(pi/4) + 5!\"'),\n }),\n handler: async (args) => {\n const { expression } = args as { expression: string }\n\n const lengthError = validateExpression(expression)\n if (lengthError) return lengthError\n\n try {\n const result = math.evaluate!(expression)\n const node = math.parse!(expression)\n const katex = node.toTex()\n return `Result: ${result}\\nKaTeX: $${katex} = ${result}$`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Translates a LaTeX/KaTeX expression into mathjs syntax, evaluates it, and returns the result.\n *\n * @remarks\n * Translates common LaTeX constructs (`\\frac{a}{b}`, `\\sqrt{...}`, `\\cdot`, `\\times`, Greek\n * macros like `\\pi`, inverse trig `\\arcsin`/`\\arccos`/`\\arctan`, `\\left`/`\\right` delimiters,\n * `\\text{...}`, subscripts, etc.) into their mathjs equivalents before evaluation. Both the source\n * and the translated mathjs expression are subject to the 1000-character length cap.\n *\n * Also evaluates three calculus constructs **numerically** (mathjs has no symbolic integration, and\n * its symbolic `derivative` is blocklisted here for safety):\n * - Definite integrals — `\\int_{a}^{b} f \\,dx` via composite Simpson quadrature.\n * - Derivatives at a point — `\\frac{d}{dx} f \\big|_{x=a}` via central finite difference.\n * - Limits — `\\lim_{x \\to a} f` (including `a = \\pm\\infty`) via a two-sided numeric approach.\n *\n * Numeric results are rounded with `math.format(..., { precision: 12 })` and labelled\n * `Result (numeric):` to flag that they are approximations. Constructs that cannot be evaluated\n * numerically (indefinite integrals, derivatives without a point, infinite integration bounds,\n * singular integrands, divergent limits) return a specific, guiding error string.\n *\n * Parse and evaluation errors are returned as error strings rather than thrown.\n */\nexport const evaluateKatexTool = new Tool({\n name: 'evaluate_katex',\n description:\n 'Evaluate a KaTeX/LaTeX math expression and return the numeric result. Supports arithmetic, trig, logs, roots, and numeric calculus: definite integrals (\\\\int_{a}^{b} f dx), derivatives at a point (\\\\frac{d}{dx} f|_{x=a}), and limits (\\\\lim_{x \\\\to a} f).',\n inputSchema: validator.object({\n katex: validator\n .string()\n .required()\n .description('LaTeX expression, e.g. \"\\\\frac{1}{2} + \\\\sqrt{9}\" or \"\\\\int_{0}^{1} x^2 dx\"'),\n }),\n handler: async (args) => {\n const { katex } = args as { katex: string }\n\n const lengthError = validateExpression(katex)\n if (lengthError) return lengthError\n\n try {\n // Calculus (\\int, \\lim, d/dx) is detected on the raw LaTeX and evaluated numerically before\n // the scalar flattening path, which would otherwise mangle bounds/targets. Returns null for\n // non-calculus input, falling through to the scalar evaluator below unchanged.\n const calculus = tryCalculus(katex)\n if (calculus !== null) return calculus\n\n const mathjsExpr = latexToMathjs(katex)\n\n const translatedLengthError = validateExpression(mathjsExpr)\n if (translatedLengthError) return translatedLengthError\n\n const result = math.evaluate!(mathjsExpr)\n return `Converted: ${mathjsExpr}\\nResult: ${result}`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n"],"mappings":"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"}
|
package/batteries/tools/math.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { o as isError } from "../../tool_registry-
|
|
2
|
-
import { t as Tool } from "../../tool-
|
|
3
|
-
import "../../common-
|
|
1
|
+
import { o as isError, s as isInstanceOf } from "../../tool_registry-BGHg6KTq.mjs";
|
|
2
|
+
import { t as Tool } from "../../tool-CRZSUcdP.mjs";
|
|
3
|
+
import "../../common-lMrnzoyn.mjs";
|
|
4
4
|
import "../../guards.mjs";
|
|
5
5
|
import { validator } from "@nhtio/validation";
|
|
6
6
|
import { all, create } from "mathjs";
|
|
@@ -43,7 +43,10 @@ function latexToMathjs(latex) {
|
|
|
43
43
|
}
|
|
44
44
|
expr = expr.replace(/\\sqrt\[([^\]]+)\]\s*\{([^{}]*)\}/g, "nthRoot($2, $1)");
|
|
45
45
|
expr = expr.replace(/\\sqrt\s*\{([^{}]*)\}/g, "sqrt($1)");
|
|
46
|
-
expr = expr.replace(/\\
|
|
46
|
+
expr = expr.replace(/\\arcsin/g, "asin");
|
|
47
|
+
expr = expr.replace(/\\arccos/g, "acos");
|
|
48
|
+
expr = expr.replace(/\\arctan/g, "atan");
|
|
49
|
+
expr = expr.replace(/\\(sin|cos|tan|cot|sec|csc|sinh|cosh|tanh|ln|log|exp|abs|det)/g, "$1");
|
|
47
50
|
expr = expr.replace(/log_\{?(\w+)\}?\s*\(([^)]+)\)/g, "log($2, $1)");
|
|
48
51
|
expr = expr.replace(/\^{([^{}]*)}/g, "^($1)");
|
|
49
52
|
expr = expr.replace(/_\{[^{}]*\}/g, "");
|
|
@@ -68,6 +71,222 @@ function latexToMathjs(latex) {
|
|
|
68
71
|
expr = expr.replace(/\s+/g, " ").trim();
|
|
69
72
|
return expr;
|
|
70
73
|
}
|
|
74
|
+
/** A tagged error whose message is surfaced to the model verbatim (caught by {@link tryCalculus}). */
|
|
75
|
+
var CalculusError = class extends Error {};
|
|
76
|
+
/** Evaluate a mathjs expression at a single point, guarding against non-finite results. */
|
|
77
|
+
function evalAt(expr, varName, x) {
|
|
78
|
+
const y = math.evaluate(expr, { [varName]: x });
|
|
79
|
+
if (typeof y !== "number" || !Number.isFinite(y)) throw new CalculusError("NON_FINITE");
|
|
80
|
+
return y;
|
|
81
|
+
}
|
|
82
|
+
/** Composite Simpson's rule over a finite interval. Throws {@link CalculusError} on a singularity. */
|
|
83
|
+
function simpson(fn, a, b, n = 1e3) {
|
|
84
|
+
if (n % 2 === 1) n++;
|
|
85
|
+
const h = (b - a) / n;
|
|
86
|
+
let sum = fn(a) + fn(b);
|
|
87
|
+
for (let i = 1; i < n; i++) sum += (i % 2 === 1 ? 4 : 2) * fn(a + i * h);
|
|
88
|
+
const result = h / 3 * sum;
|
|
89
|
+
if (!Number.isFinite(result)) throw new CalculusError("NON_FINITE");
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/** Round numeric output to 12 significant digits, collapsing floating-point noise. */
|
|
93
|
+
function formatNumeric(n) {
|
|
94
|
+
if (Math.abs(n) < 1e-9) return "0";
|
|
95
|
+
return math.format(n, { precision: 12 });
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Reads a single `_`/`^` script starting at `i`, supporting braced (`_{0}`, `^{\pi}`), command
|
|
99
|
+
* (`^\pi`), and bare-token (`_0`, `^1`) forms. Returns the mark, its raw-LaTeX value, and the next
|
|
100
|
+
* index, or `null` when there is no script at `i`.
|
|
101
|
+
*/
|
|
102
|
+
function readScript(s, i) {
|
|
103
|
+
while (s[i] === " ") i++;
|
|
104
|
+
const mark = s[i];
|
|
105
|
+
if (mark !== "_" && mark !== "^") return null;
|
|
106
|
+
i++;
|
|
107
|
+
while (s[i] === " ") i++;
|
|
108
|
+
if (s[i] === "{") {
|
|
109
|
+
let depth = 0;
|
|
110
|
+
const start = ++i;
|
|
111
|
+
for (; i < s.length; i++) if (s[i] === "{") depth++;
|
|
112
|
+
else if (s[i] === "}") {
|
|
113
|
+
if (depth === 0) break;
|
|
114
|
+
depth--;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
mark,
|
|
118
|
+
val: s.slice(start, i),
|
|
119
|
+
next: i + 1
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (s[i] === "\\") {
|
|
123
|
+
const m = /^\\[a-zA-Z]+/.exec(s.slice(i));
|
|
124
|
+
if (!m) return null;
|
|
125
|
+
return {
|
|
126
|
+
mark,
|
|
127
|
+
val: m[0],
|
|
128
|
+
next: i + m[0].length
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const m = /^[A-Za-z0-9.]+/.exec(s.slice(i));
|
|
132
|
+
if (!m) return null;
|
|
133
|
+
return {
|
|
134
|
+
mark,
|
|
135
|
+
val: m[0],
|
|
136
|
+
next: i + m[0].length
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** Translate a raw-LaTeX bound/target fragment and evaluate it to a finite number. */
|
|
140
|
+
function evalBound(latex) {
|
|
141
|
+
const trimmed = latex.trim();
|
|
142
|
+
if (/\\infty/.test(trimmed)) throw new CalculusError("INFINITE_BOUND");
|
|
143
|
+
const mathjsExpr = latexToMathjs(trimmed);
|
|
144
|
+
const value = math.evaluate(mathjsExpr);
|
|
145
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new CalculusError("BAD_BOUND");
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
/** Evaluate a definite integral `\int_{a}^{b} f \,dx` by Simpson quadrature. */
|
|
149
|
+
function evalIntegral(s) {
|
|
150
|
+
const m = /\\int(?:\\limits)?/.exec(s);
|
|
151
|
+
if (!m) throw new CalculusError("NOT_INTEGRAL");
|
|
152
|
+
let i = m.index + m[0].length;
|
|
153
|
+
const scripts = {
|
|
154
|
+
"_": void 0,
|
|
155
|
+
"^": void 0
|
|
156
|
+
};
|
|
157
|
+
for (let k = 0; k < 2; k++) {
|
|
158
|
+
const sc = readScript(s, i);
|
|
159
|
+
if (!sc) break;
|
|
160
|
+
scripts[sc.mark] = sc.val;
|
|
161
|
+
i = sc.next;
|
|
162
|
+
}
|
|
163
|
+
if (scripts._ === void 0 || scripts["^"] === void 0) throw new CalculusError("INDEFINITE");
|
|
164
|
+
const rest = s.slice(i);
|
|
165
|
+
const diff = /\\?,?\s*\bd\s*([a-zA-Z])\s*$/.exec(rest);
|
|
166
|
+
if (!diff) throw new CalculusError("NO_DIFFERENTIAL");
|
|
167
|
+
const variable = diff[1];
|
|
168
|
+
const integrandLatex = rest.slice(0, diff.index);
|
|
169
|
+
if (integrandLatex.trim().length === 0) throw new CalculusError("NO_INTEGRAND");
|
|
170
|
+
const a = evalBound(scripts._);
|
|
171
|
+
const b = evalBound(scripts["^"]);
|
|
172
|
+
const integrand = latexToMathjs(integrandLatex);
|
|
173
|
+
const fn = (x) => evalAt(integrand, variable, x);
|
|
174
|
+
let result;
|
|
175
|
+
if (a === b) result = 0;
|
|
176
|
+
else if (a < b) result = simpson(fn, a, b);
|
|
177
|
+
else result = -simpson(fn, b, a);
|
|
178
|
+
return `Converted: ∫(${integrand}) d${variable} from ${formatNumeric(a)} to ${formatNumeric(b)}\nResult (numeric): ${formatNumeric(result)}`;
|
|
179
|
+
}
|
|
180
|
+
/** Evaluate a derivative `\frac{d}{dx} f \big|_{x=a}` at a point via central finite difference. */
|
|
181
|
+
function evalDerivative(s) {
|
|
182
|
+
let variable;
|
|
183
|
+
let body = s;
|
|
184
|
+
const fracOp = /\\frac\s*\{\s*d\s*\}\s*\{\s*d\s*([a-zA-Z])\s*\}/.exec(s);
|
|
185
|
+
if (fracOp) {
|
|
186
|
+
variable = fracOp[1];
|
|
187
|
+
body = s.slice(fracOp.index + fracOp[0].length);
|
|
188
|
+
} else {
|
|
189
|
+
const bareOp = /(?:^|[^a-zA-Z])d\s*\/\s*d([a-zA-Z])/.exec(s);
|
|
190
|
+
if (bareOp) {
|
|
191
|
+
variable = bareOp[1];
|
|
192
|
+
body = s.slice(bareOp.index + bareOp[0].length);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!variable) throw new CalculusError("NOT_DERIVATIVE");
|
|
196
|
+
body = body.replace(/\\left\.?/g, "").replace(/\\right\.?/g, "");
|
|
197
|
+
const bar = /\\?(?:Big|big|bigg|Bigg)?\s*\|\s*_\s*\{?\s*([a-zA-Z])\s*=\s*([^}]+?)\s*\}?\s*$/.exec(body);
|
|
198
|
+
if (!bar) throw new CalculusError("NO_POINT");
|
|
199
|
+
const point = evalBound(bar[2]);
|
|
200
|
+
let fnLatex = body.slice(0, bar.index).trim();
|
|
201
|
+
fnLatex = fnLatex.replace(/^\\?\(([\s\S]*)\\?\)$/, "$1").replace(/^\[([\s\S]*)\]$/, "$1").trim();
|
|
202
|
+
if (fnLatex.length === 0) throw new CalculusError("NO_FUNCTION");
|
|
203
|
+
const expr = latexToMathjs(fnLatex);
|
|
204
|
+
const h = 1e-6;
|
|
205
|
+
const fn = (x) => evalAt(expr, variable, x);
|
|
206
|
+
const result = (fn(point + h) - fn(point - h)) / (2 * h);
|
|
207
|
+
if (!Number.isFinite(result)) throw new CalculusError("NON_FINITE");
|
|
208
|
+
return `Converted: d/d${variable}(${expr}) at ${variable}=${formatNumeric(point)}\nResult (numeric): ${formatNumeric(result)}`;
|
|
209
|
+
}
|
|
210
|
+
/** Evaluate a limit `\lim_{x \to a} f(x)` by a two-sided numeric approach. */
|
|
211
|
+
function evalLimit(s) {
|
|
212
|
+
const head = /\\lim\s*_\s*\{\s*([a-zA-Z])\s*\\to\s*([\s\S]+?)\s*\}/.exec(s);
|
|
213
|
+
if (!head) throw new CalculusError("NOT_LIMIT");
|
|
214
|
+
const variable = head[1];
|
|
215
|
+
let targetLatex = head[2].trim();
|
|
216
|
+
const bodyLatex = s.slice(head.index + head[0].length).trim();
|
|
217
|
+
if (bodyLatex.length === 0) throw new CalculusError("NO_FUNCTION");
|
|
218
|
+
let side = "both";
|
|
219
|
+
const oneSided = /\^\s*\{?\s*([+-])\s*\}?\s*$/.exec(targetLatex);
|
|
220
|
+
if (oneSided) {
|
|
221
|
+
side = oneSided[1] === "+" ? "plus" : "minus";
|
|
222
|
+
targetLatex = targetLatex.slice(0, oneSided.index).trim();
|
|
223
|
+
}
|
|
224
|
+
const expr = latexToMathjs(bodyLatex);
|
|
225
|
+
const fn = (x) => evalAt(expr, variable, x);
|
|
226
|
+
const eps = 1e-6;
|
|
227
|
+
let result;
|
|
228
|
+
let targetLabel;
|
|
229
|
+
if (/^-\s*\\infty$/.test(targetLatex)) {
|
|
230
|
+
result = fn(-1e8);
|
|
231
|
+
targetLabel = "-∞";
|
|
232
|
+
} else if (/^\\infty$/.test(targetLatex)) {
|
|
233
|
+
result = fn(1e8);
|
|
234
|
+
targetLabel = "∞";
|
|
235
|
+
} else {
|
|
236
|
+
const target = evalBound(targetLatex);
|
|
237
|
+
targetLabel = formatNumeric(target);
|
|
238
|
+
if (side === "plus") result = fn(target + eps);
|
|
239
|
+
else if (side === "minus") result = fn(target - eps);
|
|
240
|
+
else {
|
|
241
|
+
const lo = fn(target - eps);
|
|
242
|
+
const hi = fn(target + eps);
|
|
243
|
+
const avg = (lo + hi) / 2;
|
|
244
|
+
if (Math.abs(hi - lo) > .001 * Math.max(1, Math.abs(avg))) throw new CalculusError("LIMIT_MISMATCH");
|
|
245
|
+
result = avg;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!Number.isFinite(result)) throw new CalculusError("LIMIT_UNBOUNDED");
|
|
249
|
+
const nearest = Math.round(result);
|
|
250
|
+
if (Math.abs(result - nearest) < 1e-6) result = nearest;
|
|
251
|
+
return `Converted: lim ${variable}→${targetLabel} of (${expr})\nResult (numeric): ${formatNumeric(result)}`;
|
|
252
|
+
}
|
|
253
|
+
/** Human-readable, model-actionable message for each {@link CalculusError} tag. */
|
|
254
|
+
function calculusErrorMessage(tag) {
|
|
255
|
+
switch (tag) {
|
|
256
|
+
case "INDEFINITE": return "Cannot evaluate an indefinite integral numerically. Provide bounds, e.g. \\int_{0}^{1} x dx.";
|
|
257
|
+
case "NO_DIFFERENTIAL": return "Could not find the integration variable. Expected a trailing differential like 'dx'.";
|
|
258
|
+
case "NO_INTEGRAND": return "The integral has no integrand to evaluate.";
|
|
259
|
+
case "INFINITE_BOUND": return "Infinite integration bounds are not supported. Provide finite numeric bounds, e.g. \\int_{0}^{1} f dx.";
|
|
260
|
+
case "BAD_BOUND": return "Could not evaluate the integration bounds to numbers.";
|
|
261
|
+
case "NO_POINT": return "Cannot evaluate a derivative numerically without a point. Specify where, e.g. \\frac{d}{dx}(x^2)\\Big|_{x=3}.";
|
|
262
|
+
case "NO_FUNCTION": return "Could not find the function to evaluate.";
|
|
263
|
+
case "LIMIT_MISMATCH": return "The limit may not exist (left and right values disagree).";
|
|
264
|
+
case "LIMIT_UNBOUNDED": return "The limit appears to diverge (function is unbounded near the target).";
|
|
265
|
+
case "NON_FINITE": return "The expression is not finite over the requested range (possible singularity); cannot evaluate numerically.";
|
|
266
|
+
default: return "Could not evaluate the calculus expression.";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Detects a calculus construct in raw LaTeX and routes it to the matching numeric handler. Returns
|
|
271
|
+
* the result/error string, or `null` when the input is not a calculus expression (so the caller
|
|
272
|
+
* falls through to the scalar evaluation path unchanged).
|
|
273
|
+
*/
|
|
274
|
+
function tryCalculus(latex) {
|
|
275
|
+
let s = latex.trim();
|
|
276
|
+
s = s.replace(/^\$\$?|\$\$?$/g, "");
|
|
277
|
+
s = s.replace(/^\\[[(]|\\[\])]$/g, "").trim();
|
|
278
|
+
let handler;
|
|
279
|
+
if (/\\int/.test(s)) handler = evalIntegral;
|
|
280
|
+
else if (/\\lim/.test(s)) handler = evalLimit;
|
|
281
|
+
else if (/\\frac\s*\{\s*d\s*\}\s*\{\s*d\s*[a-zA-Z]\s*\}/.test(s) || /(?:^|[^a-zA-Z])d\s*\/\s*d[a-zA-Z]/.test(s)) handler = evalDerivative;
|
|
282
|
+
if (!handler) return null;
|
|
283
|
+
try {
|
|
284
|
+
return handler(s);
|
|
285
|
+
} catch (err) {
|
|
286
|
+
if (isInstanceOf(err, "CalculusError", CalculusError)) return `Error: ${calculusErrorMessage(err.message)}`;
|
|
287
|
+
return `Error: ${isError(err) ? err.message : String(err)}`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
71
290
|
/**
|
|
72
291
|
* Evaluates a mathjs-syntax expression and returns the numeric result alongside the KaTeX
|
|
73
292
|
* representation of the parsed expression.
|
|
@@ -103,21 +322,34 @@ var calculateTool = new Tool({
|
|
|
103
322
|
*
|
|
104
323
|
* @remarks
|
|
105
324
|
* Translates common LaTeX constructs (`\frac{a}{b}`, `\sqrt{...}`, `\cdot`, `\times`, Greek
|
|
106
|
-
* macros like `\pi`, `\left`/`\right` delimiters,
|
|
107
|
-
* mathjs equivalents before evaluation. Both the source
|
|
108
|
-
* are subject to the 1000-character length cap.
|
|
325
|
+
* macros like `\pi`, inverse trig `\arcsin`/`\arccos`/`\arctan`, `\left`/`\right` delimiters,
|
|
326
|
+
* `\text{...}`, subscripts, etc.) into their mathjs equivalents before evaluation. Both the source
|
|
327
|
+
* and the translated mathjs expression are subject to the 1000-character length cap.
|
|
328
|
+
*
|
|
329
|
+
* Also evaluates three calculus constructs **numerically** (mathjs has no symbolic integration, and
|
|
330
|
+
* its symbolic `derivative` is blocklisted here for safety):
|
|
331
|
+
* - Definite integrals — `\int_{a}^{b} f \,dx` via composite Simpson quadrature.
|
|
332
|
+
* - Derivatives at a point — `\frac{d}{dx} f \big|_{x=a}` via central finite difference.
|
|
333
|
+
* - Limits — `\lim_{x \to a} f` (including `a = \pm\infty`) via a two-sided numeric approach.
|
|
334
|
+
*
|
|
335
|
+
* Numeric results are rounded with `math.format(..., { precision: 12 })` and labelled
|
|
336
|
+
* `Result (numeric):` to flag that they are approximations. Constructs that cannot be evaluated
|
|
337
|
+
* numerically (indefinite integrals, derivatives without a point, infinite integration bounds,
|
|
338
|
+
* singular integrands, divergent limits) return a specific, guiding error string.
|
|
109
339
|
*
|
|
110
340
|
* Parse and evaluation errors are returned as error strings rather than thrown.
|
|
111
341
|
*/
|
|
112
342
|
var evaluateKatexTool = new Tool({
|
|
113
343
|
name: "evaluate_katex",
|
|
114
|
-
description: "Evaluate a KaTeX/LaTeX math expression and return the numeric result.",
|
|
115
|
-
inputSchema: validator.object({ katex: validator.string().required().description("LaTeX expression, e.g. \"\\frac{1}{2} + \\sqrt{9}\"") }),
|
|
344
|
+
description: "Evaluate a KaTeX/LaTeX math expression and return the numeric result. Supports arithmetic, trig, logs, roots, and numeric calculus: definite integrals (\\int_{a}^{b} f dx), derivatives at a point (\\frac{d}{dx} f|_{x=a}), and limits (\\lim_{x \\to a} f).",
|
|
345
|
+
inputSchema: validator.object({ katex: validator.string().required().description("LaTeX expression, e.g. \"\\frac{1}{2} + \\sqrt{9}\" or \"\\int_{0}^{1} x^2 dx\"") }),
|
|
116
346
|
handler: async (args) => {
|
|
117
347
|
const { katex } = args;
|
|
118
348
|
const lengthError = validateExpression(katex);
|
|
119
349
|
if (lengthError) return lengthError;
|
|
120
350
|
try {
|
|
351
|
+
const calculus = tryCalculus(katex);
|
|
352
|
+
if (calculus !== null) return calculus;
|
|
121
353
|
const mathjsExpr = latexToMathjs(katex);
|
|
122
354
|
const translatedLengthError = validateExpression(mathjsExpr);
|
|
123
355
|
if (translatedLengthError) return translatedLengthError;
|