lingshu-solver 1.0.14 → 1.0.15

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # 灵数求解器 · Lingshu Solver
2
2
 
3
+ **品类(Category)**:认证实根计算 · Certified Real-Root Computation
4
+
3
5
  [![License](https://img.shields.io/badge/license-非商业免费%20%2F%20商业须书面授权-blue)](LICENSE)
4
6
  [![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP%20%2B%20stdio-blue)](https://modelcontextprotocol.io)
5
7
  [![Deterministic](https://img.shields.io/badge/core-deterministic%20%2F%20non--LLM-green)](docs/03-设计思想.md)
@@ -47,7 +47,7 @@ const solverCore = require('./solver-core');
47
47
  const { solve } = solverCore;
48
48
 
49
49
  const SERVER_NAME = 'lingshu-solver';
50
- const SERVER_VERSION = '1.0.14';
50
+ const SERVER_VERSION = '1.0.15';
51
51
  const PORT = parseInt(process.env.PORT || '3000', 10);
52
52
 
53
53
  // ---- 护栏常量(防畸形/恶意输入耗尽资源,与 stdio 版一致)----
@@ -854,6 +854,85 @@ function doSolve(args) {
854
854
  return shapeResult(r);
855
855
  }
856
856
 
857
+ // ---- 新工具:poly_roots / verify(agent 时代适配,薄封装 solve,不碰内核)----
858
+ function buildPolyEquation(coeffs) {
859
+ const n = coeffs.length - 1;
860
+ let s = '';
861
+ for (let i = 0; i < coeffs.length; i++) {
862
+ const c = coeffs[i]; const deg = n - i;
863
+ if (c === 0 && deg !== 0) continue;
864
+ const abs = Math.abs(c);
865
+ const sign = (s === '' ? (c < 0 ? '-' : '') : (c < 0 ? ' - ' : ' + '));
866
+ const term = deg === 0 ? '' + abs : deg === 1 ? abs + '*x' : abs + '*x^' + deg;
867
+ s += sign + term;
868
+ }
869
+ return s + ' = 0';
870
+ }
871
+
872
+ function doPolyRoots(args) {
873
+ const coeffs = args && args.coefficients;
874
+ if (!Array.isArray(coeffs) || coeffs.length < 2) throw { type: 'invalid_input', message: 'coefficients 必须是长度≥2 的数组(最高次系数在前)' };
875
+ for (const c of coeffs) if (typeof c !== 'number' || !isFinite(c)) throw { type: 'invalid_input', message: 'coefficients 须为有限数字' };
876
+ const eq = buildPolyEquation(coeffs);
877
+ const r = solve([eq], ['x'], 6, undefined, false, {});
878
+ return shapeResult(r);
879
+ }
880
+
881
+ function doVerify(args) {
882
+ const eq = args && args.equation;
883
+ if (typeof eq !== 'string' || !eq.includes('=')) throw { type: 'invalid_input', message: 'equation 须为含 "=" 的字符串' };
884
+ const cand = args.candidate;
885
+ let varNames, candPoint;
886
+ if (typeof cand === 'number') {
887
+ varNames = (args && Array.isArray(args.variables) && args.variables[0]) ? [args.variables[0]] : ['x'];
888
+ candPoint = [cand];
889
+ } else if (cand && typeof cand === 'object' && !Array.isArray(cand)) {
890
+ varNames = Object.keys(cand);
891
+ candPoint = varNames.map(function (v) { return cand[v]; });
892
+ } else if (Array.isArray(cand)) {
893
+ varNames = (args && Array.isArray(args.variables)) ? args.variables : [];
894
+ if (varNames.length !== cand.length) throw { type: 'invalid_input', message: 'candidate 数组长度须与 variables 一致' };
895
+ candPoint = cand;
896
+ } else {
897
+ throw { type: 'invalid_input', message: 'candidate 须为数字 / {变量:值} / [值...]' };
898
+ }
899
+ const margin = (args && typeof args.tolerance === 'number' && args.tolerance > 0) ? args.tolerance : 1e-3;
900
+ const domain = {};
901
+ varNames.forEach(function (v, i) { domain[v] = [candPoint[i] - margin, candPoint[i] + margin]; });
902
+ const r = solve([eq], varNames, 6, domain, false, {});
903
+ const shaped = shapeResult(r);
904
+ const matched = (r.solutions || []).find(function (s) {
905
+ return Array.isArray(s.values) && s.values.every(function (val, i) { return Math.abs(val - candPoint[i]) <= 1e-6; });
906
+ });
907
+ if (matched) {
908
+ return {
909
+ verdict: 'verified', isRoot: true, candidate: cand,
910
+ matchedRoot: { values: matched.values, tier: matched.tier, certified: !!matched.certified, cert: matched.cert || null,
911
+ text: varNames.map(function (vn, i) { return vn + '=' + fmt6(matched.values[i]); }).join(', ') },
912
+ reportId: shaped.reportId, certification: shaped.certification
913
+ };
914
+ }
915
+ let nearest = null;
916
+ try {
917
+ // refuted:在更宽域(默认 ±1e6)重算,给 Agent 全局最近的「真认证根」——
918
+ // 否则当 LLM 把根算偏(如猜 2.1、真根 2)时,窄邻域内无解会导致 nearest 为空,
919
+ // Agent 看不到正确值,verify 就失去了「纠正 LLM」的核心价值。
920
+ const rb = solve([eq], varNames, 6, undefined, false, {});
921
+ const solsB = (rb.solutions || []).filter(function (s) { return Array.isArray(s.values); });
922
+ if (solsB.length) {
923
+ let bestD = Infinity, bestS = null;
924
+ for (const s of solsB) { let d = 0; for (let i = 0; i < s.values.length; i++) d += (s.values[i] - candPoint[i]) * (s.values[i] - candPoint[i]); if (d < bestD) { bestD = d; bestS = s; } }
925
+ nearest = { values: bestS.values, tier: bestS.tier || null, certified: !!bestS.certified, cert: bestS.cert || null,
926
+ text: varNames.map(function (vn, i) { return vn + '=' + fmt6(bestS.values[i]); }).join(', ') };
927
+ }
928
+ } catch (_e) { /* 宽域重算失败不致命,nearest 保持 null */ }
929
+ return {
930
+ verdict: 'refuted_or_unverified', isRoot: false, candidate: cand,
931
+ message: '在候选点 ±' + margin + ' 邻域内未找到与之匹配的认证根;候选不是经验证的实根。' + (nearest ? '(全局最近认证根见 nearestCertifiedRoot)' : '(该方程在默认域内也无实根)'),
932
+ nearestCertifiedRoot: nearest, reportId: shaped.reportId
933
+ };
934
+ }
935
+
857
936
  // ---- 工具定义(与 stdio 版一致)----
858
937
  const TOOLS = [
859
938
  {
@@ -901,6 +980,34 @@ const TOOLS = [
901
980
  required: ['message']
902
981
  }
903
982
  },
983
+ {
984
+ name: 'poly_roots',
985
+ description: '多项式全部实根,逐个 Krawczyk 认证(带严格误差盒)。输入系数「最高次在前」,如 [1,-2,-5,6] 表示 x³−2x²−5x+6。确定性、可复现、可证明正确;复数根不返回(本品只做实数)。给 AI Agent 当「可靠的多项式求根件」——不会再像通用 LLM 那样把根算错或半对。' +
986
+ '★ 计费边界(Agent 必读):与 solve 同规则 —— honorPaid:true 即信任制免费放行;否则走付费通道(GET /pricing)。',
987
+ inputSchema: {
988
+ type: 'object',
989
+ properties: {
990
+ coefficients: { type: 'array', items: { type: 'number' }, description: '多项式系数,最高次在前。如 [1,-2,-5,6] 对应 x³−2x²−5x+6。' },
991
+ tolerance: { type: 'number', description: '根的判定容差(可选,默认内部精度)' }
992
+ },
993
+ required: ['coefficients']
994
+ }
995
+ },
996
+ {
997
+ name: 'verify',
998
+ description: '核验一个「声称的答案」到底对不对——这是「LLM 猜了 2.1、真根是精确 2」的检查。输入方程 + 候选值/点(数字,或 {变量:值},或按 variables 顺序的数组),本工具在候选点邻域内调用同一套认证内核:若找到与之匹配的认证根 → 返回 verified + 误差盒;若找不到 → 返回 refuted 并附上最近的认证根(让 Agent 立刻看到正确值)。确定性、非 LLM、结果可复现。' +
999
+ '★ 计费边界(Agent 必读):与 solve 同规则 —— honorPaid:true 即信任制免费放行;否则走付费通道(GET /pricing)。',
1000
+ inputSchema: {
1001
+ type: 'object',
1002
+ properties: {
1003
+ equation: { type: 'string', description: '含 "=" 的方程,如 "x^2 = 4"。' },
1004
+ candidate: { description: '声称的答案:数字(单变量,默认变量 x)、{变量:值}(多变量)、或按 variables 顺序的数组。', oneOf: [{ type: 'number' }, { type: 'object' }, { type: 'array' }] },
1005
+ variables: { type: 'array', items: { type: 'string' }, description: '变量名(多变量或数组候选时必填),如 ["x","y"]。' },
1006
+ tolerance: { type: 'number', description: '邻域半径(可选,默认 1e-3),在该邻域内寻找匹配的认证根。' }
1007
+ },
1008
+ required: ['equation', 'candidate']
1009
+ }
1010
+ },
904
1011
  {
905
1012
  name: 'pay',
906
1013
  description: '对公收款:为本次/后续求解创建一笔真实付费订单(' + yuan(PRICE_CENTS) + '/次),返回订单号、专属 key 与结构化付款意图(payIntent)。' +
@@ -967,15 +1074,22 @@ function handleRpc(msg, ip, ctx) {
967
1074
  let honorClaimed = false;
968
1075
  try {
969
1076
  let result;
970
- if (name === 'solve') {
1077
+ if (name === 'solve' || name === 'poly_roots' || name === 'verify') {
971
1078
  // ── 信任制(honor system):调用方声明「我已付款」即放行,不验证、不扣余额。
972
1079
  // 设计取舍(用户决策):没有支付通道凭证、又想直接用的人,给一个零摩擦入口。
973
1080
  // 最坏情况=白嫖(求解是确定性离线计算,边际成本≈0,不亏);最好情况=有人真去
974
1081
  // 付静态收款码那 1 分。转化靠 calls.log 里的 honorClaimed 计数衡量。
1082
+ // poly_roots / verify 与 solve 同属「认证数值计算」,共用信任制 + 计费路径。
1083
+ const isSolve = (name === 'solve');
1084
+ const runSolver = function () {
1085
+ if (name === 'solve') return doSolve(args);
1086
+ if (name === 'poly_roots') return doPolyRoots(args);
1087
+ return doVerify(args);
1088
+ };
975
1089
  const honor = !!(args && args.honorPaid === true);
976
1090
  if (honor) {
977
- result = doSolve(args);
978
- result.support = honorSupportBlock(); // 层1:代传微请求,借 Agent「忠实转发器」特性触达付款人
1091
+ result = runSolver();
1092
+ if (isSolve) result.support = honorSupportBlock(); // 层1:代传微请求,借 Agent「忠实转发器」特性触达付款人
979
1093
  freeCall = true;
980
1094
  honorClaimed = true;
981
1095
  honorClaims++;
@@ -984,7 +1098,7 @@ function handleRpc(msg, ip, ctx) {
984
1098
  const meter = meterPreCheck(ctx);
985
1099
  if (!meter.ok) throw meter.err; // 落到下方 catch → isError,不扣费
986
1100
  freeCall = !!meter.free;
987
- result = doSolve(args);
1101
+ result = runSolver();
988
1102
  // 只有成功产出结果、且不是「解析失败」才扣
989
1103
  chargedCents = (meter.free || !isBillable(result)) ? 0 : meterCharge(ctx);
990
1104
  }
package/index.html CHANGED
@@ -11192,7 +11192,7 @@ function _runTail(state) {
11192
11192
 
11193
11193
  // —— 可审计结构化溯源(切片A):每个结果附带完整证据链元数据 ——
11194
11194
  // 设计目标:Agent / 上层系统拿到的不只是裸数字,而是"为什么是这个解"的可读证据链。
11195
- var SOLVER_VERSION = "lingshu-solver/1.0.14";
11195
+ var SOLVER_VERSION = "lingshu-solver/1.0.15";
11196
11196
 
11197
11197
  // —— 确定性可复现契约(Certified Real-Root Computation 六属性之「确定性可复现」)——
11198
11198
  // 纯 JS 同步 SHA-256(零依赖,浏览器/Node 通用,免 Web Crypto 异步)。
package/mcp-server.js CHANGED
@@ -19,7 +19,7 @@ const path = require('path');
19
19
  const { solve } = require('./solver-core');
20
20
 
21
21
  const SERVER_NAME = 'lingshu-solver';
22
- const SERVER_VERSION = '1.0.14';
22
+ const SERVER_VERSION = '1.0.15';
23
23
 
24
24
  // ---- 护栏常量(防畸形/恶意输入耗尽资源)----
25
25
  const MAX_TOTAL_CHARS = 100 * 1024; // 单次请求方程文本总长上限 100KB
@@ -150,6 +150,85 @@ function doSolve(args) {
150
150
  return shapeResult(r);
151
151
  }
152
152
 
153
+ // ---- 新工具:poly_roots / verify(agent 时代适配,薄封装 solve,不碰内核)----
154
+ function buildPolyEquation(coeffs) {
155
+ const n = coeffs.length - 1;
156
+ let s = '';
157
+ for (let i = 0; i < coeffs.length; i++) {
158
+ const c = coeffs[i]; const deg = n - i;
159
+ if (c === 0 && deg !== 0) continue;
160
+ const abs = Math.abs(c);
161
+ const sign = (s === '' ? (c < 0 ? '-' : '') : (c < 0 ? ' - ' : ' + '));
162
+ const term = deg === 0 ? '' + abs : deg === 1 ? abs + '*x' : abs + '*x^' + deg;
163
+ s += sign + term;
164
+ }
165
+ return s + ' = 0';
166
+ }
167
+
168
+ function doPolyRoots(args) {
169
+ const coeffs = args && args.coefficients;
170
+ if (!Array.isArray(coeffs) || coeffs.length < 2) throw { type: 'invalid_input', message: 'coefficients 必须是长度≥2 的数组(最高次系数在前)' };
171
+ for (const c of coeffs) if (typeof c !== 'number' || !isFinite(c)) throw { type: 'invalid_input', message: 'coefficients 须为有限数字' };
172
+ const eq = buildPolyEquation(coeffs);
173
+ const r = solve([eq], ['x'], 6, undefined, false, {});
174
+ return shapeResult(r);
175
+ }
176
+
177
+ function doVerify(args) {
178
+ const eq = args && args.equation;
179
+ if (typeof eq !== 'string' || !eq.includes('=')) throw { type: 'invalid_input', message: 'equation 须为含 "=" 的字符串' };
180
+ const cand = args.candidate;
181
+ let varNames, candPoint;
182
+ if (typeof cand === 'number') {
183
+ varNames = (args && Array.isArray(args.variables) && args.variables[0]) ? [args.variables[0]] : ['x'];
184
+ candPoint = [cand];
185
+ } else if (cand && typeof cand === 'object' && !Array.isArray(cand)) {
186
+ varNames = Object.keys(cand);
187
+ candPoint = varNames.map(function (v) { return cand[v]; });
188
+ } else if (Array.isArray(cand)) {
189
+ varNames = (args && Array.isArray(args.variables)) ? args.variables : [];
190
+ if (varNames.length !== cand.length) throw { type: 'invalid_input', message: 'candidate 数组长度须与 variables 一致' };
191
+ candPoint = cand;
192
+ } else {
193
+ throw { type: 'invalid_input', message: 'candidate 须为数字 / {变量:值} / [值...]' };
194
+ }
195
+ const margin = (args && typeof args.tolerance === 'number' && args.tolerance > 0) ? args.tolerance : 1e-3;
196
+ const domain = {};
197
+ varNames.forEach(function (v, i) { domain[v] = [candPoint[i] - margin, candPoint[i] + margin]; });
198
+ const r = solve([eq], varNames, 6, domain, false, {});
199
+ const shaped = shapeResult(r);
200
+ const matched = (r.solutions || []).find(function (s) {
201
+ return Array.isArray(s.values) && s.values.every(function (val, i) { return Math.abs(val - candPoint[i]) <= 1e-6; });
202
+ });
203
+ if (matched) {
204
+ return {
205
+ verdict: 'verified', isRoot: true, candidate: cand,
206
+ matchedRoot: { values: matched.values, tier: matched.tier, certified: !!matched.certified, cert: matched.cert || null,
207
+ text: varNames.map(function (vn, i) { return vn + '=' + fmt6(matched.values[i]); }).join(', ') },
208
+ reportId: shaped.reportId, certification: shaped.certification
209
+ };
210
+ }
211
+ let nearest = null;
212
+ try {
213
+ // refuted:在更宽域(默认 ±1e6)重算,给 Agent 全局最近的「真认证根」——
214
+ // 否则当 LLM 把根算偏(如猜 2.1、真根 2)时,窄邻域内无解会导致 nearest 为空,
215
+ // Agent 看不到正确值,verify 就失去了「纠正 LLM」的核心价值。
216
+ const rb = solve([eq], varNames, 6, undefined, false, {});
217
+ const solsB = (rb.solutions || []).filter(function (s) { return Array.isArray(s.values); });
218
+ if (solsB.length) {
219
+ let bestD = Infinity, bestS = null;
220
+ for (const s of solsB) { let d = 0; for (let i = 0; i < s.values.length; i++) d += (s.values[i] - candPoint[i]) * (s.values[i] - candPoint[i]); if (d < bestD) { bestD = d; bestS = s; } }
221
+ nearest = { values: bestS.values, tier: bestS.tier || null, certified: !!bestS.certified, cert: bestS.cert || null,
222
+ text: varNames.map(function (vn, i) { return vn + '=' + fmt6(bestS.values[i]); }).join(', ') };
223
+ }
224
+ } catch (_e) { /* 宽域重算失败不致命,nearest 保持 null */ }
225
+ return {
226
+ verdict: 'refuted_or_unverified', isRoot: false, candidate: cand,
227
+ message: '在候选点 ±' + margin + ' 邻域内未找到与之匹配的认证根;候选不是经验证的实根。' + (nearest ? '(全局最近认证根见 nearestCertifiedRoot)' : '(该方程在默认域内也无实根)'),
228
+ nearestCertifiedRoot: nearest, reportId: shaped.reportId
229
+ };
230
+ }
231
+
153
232
  // ---- 工具定义 ----
154
233
  const TOOLS = [
155
234
  {
@@ -194,6 +273,32 @@ const TOOLS = [
194
273
  },
195
274
  required: ['message']
196
275
  }
276
+ },
277
+ {
278
+ name: 'poly_roots',
279
+ description: '多项式全部实根,逐个 Krawczyk 认证(带严格误差盒)。输入系数「最高次在前」,如 [1,-2,-5,6] 表示 x³−2x²−5x+6。确定性、可复现、可证明正确;复数根不返回(本品只做实数)。给 AI Agent 当「可靠的多项式求根件」——不会再像通用 LLM 那样把根算错或半对。',
280
+ inputSchema: {
281
+ type: 'object',
282
+ properties: {
283
+ coefficients: { type: 'array', items: { type: 'number' }, description: '多项式系数,最高次在前。如 [1,-2,-5,6] 对应 x³−2x²−5x+6。' },
284
+ tolerance: { type: 'number', description: '根的判定容差(可选,默认内部精度)' }
285
+ },
286
+ required: ['coefficients']
287
+ }
288
+ },
289
+ {
290
+ name: 'verify',
291
+ description: '核验一个「声称的答案」到底对不对——这是「LLM 猜了 2.1、真根是精确 2」的检查。输入方程 + 候选值/点(数字,或 {变量:值},或按 variables 顺序的数组),本工具在候选点邻域内调用同一套认证内核:若找到与之匹配的认证根 → 返回 verified + 误差盒;若找不到 → 返回 refuted 并附上最近的认证根(让 Agent 立刻看到正确值)。确定性、非 LLM、结果可复现。',
292
+ inputSchema: {
293
+ type: 'object',
294
+ properties: {
295
+ equation: { type: 'string', description: '含 "=" 的方程,如 "x^2 = 4"。' },
296
+ candidate: { description: '声称的答案:数字(单变量,默认变量 x)、{变量:值}(多变量)、或按 variables 顺序的数组。', oneOf: [{ type: 'number' }, { type: 'object' }, { type: 'array' }] },
297
+ variables: { type: 'array', items: { type: 'string' }, description: '变量名(多变量或数组候选时必填),如 ["x","y"]。' },
298
+ tolerance: { type: 'number', description: '邻域半径(可选,默认 1e-3),在该邻域内寻找匹配的认证根。' }
299
+ },
300
+ required: ['equation', 'candidate']
301
+ }
197
302
  }
198
303
  ];
199
304
 
@@ -240,6 +345,10 @@ function handle(msg) {
240
345
  ts: new Date().toISOString(), message: msg_fb, context: args.context || null
241
346
  }) + '\n');
242
347
  result = { acknowledged: true, note: '反馈已记录(本地,不外传)' };
348
+ } else if (name === 'poly_roots') {
349
+ result = doPolyRoots(args);
350
+ } else if (name === 'verify') {
351
+ result = doVerify(args);
243
352
  } else {
244
353
  throw { type: 'unknown_tool', message: '未知工具: ' + name };
245
354
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lingshu-solver",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "灵数求解器 · 确定性实数方程组求解 MCP 工具(≤6变量):区间收缩 + Krawczyk 认证,离线零依赖零数据,诚实标记 truncated。本地 stdio 永久免费;另有常驻托管端点 https://hongchenlingjing.com/mcp(按次 ¥0.01/次,支持 honorPaid 免费放行)。",
5
5
  "homepage": "https://genesis-plan.github.io/lingshu-solver/",
6
6
  "repository": {