dsh-math-calculator 2.1.1 → 2.2.0
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 +921 -0
- package/README.md +64 -50
- package/dsh.plugin.json +2 -2
- package/lib/__wtest.txt +1 -0
- package/lib/awareness.js +69 -20
- package/lib/format.js +522 -13
- package/lib/formula-library-em.js +428 -0
- package/lib/formulas.js +28 -1
- package/lib/guards.js +653 -0
- package/lib/help.js +509 -0
- package/lib/index.js +1357 -212
- package/lib/ops/shared.js +547 -6
- package/lib/ops/units-op.js +116 -10
- package/lib/ops/workspace-op.js +10 -0
- package/lib/playbook.js +32 -7
- package/lib/types/format.d.ts +31 -1
- package/lib/types/formula-library-em.d.ts +28 -0
- package/lib/types/formulas.d.ts +2 -0
- package/lib/types/guards.d.ts +255 -0
- package/lib/types/help.d.ts +70 -0
- package/lib/types/index.d.ts +34 -1
- package/lib/types/ops/shared.d.ts +87 -3
- package/lib/types/playbook.d.ts +28 -0
- package/lib/types/units-data.d.ts +253 -0
- package/lib/types/units.d.ts +54 -4
- package/lib/types/workspace.d.ts +50 -0
- package/lib/units-data.js +1037 -0
- package/lib/units.js +82 -117
- package/lib/workspace.js +54 -0
- package/package.json +1 -1
- package/src/awareness.ts +69 -20
- package/src/format.ts +520 -12
- package/src/formula-library-em.ts +435 -0
- package/src/formulas.ts +35 -4
- package/src/guards.ts +673 -0
- package/src/help.ts +543 -0
- package/src/index.ts +1492 -213
- package/src/ops/shared.ts +589 -9
- package/src/ops/units-op.ts +125 -9
- package/src/ops/workspace-op.ts +10 -0
- package/src/playbook.ts +32 -7
- package/src/units-data.ts +1128 -0
- package/src/units.ts +149 -130
- package/src/workspace.ts +65 -0
package/src/ops/units-op.ts
CHANGED
|
@@ -34,7 +34,20 @@ import {
|
|
|
34
34
|
import { CalcError, safeNumberForJson, type MathArgs, type Outcome } from './shared.js'
|
|
35
35
|
|
|
36
36
|
/** 量纲的展示顺序(先写日常用的)。 */
|
|
37
|
-
const ORDER: Dimension[] = [
|
|
37
|
+
const ORDER: Dimension[] = [
|
|
38
|
+
// 力学与几何
|
|
39
|
+
'length', 'mass', 'time', 'area', 'volume',
|
|
40
|
+
'energy', 'power', 'temperature', 'speed', 'pressure',
|
|
41
|
+
'force', 'angle', 'data', 'frequency',
|
|
42
|
+
// 电学
|
|
43
|
+
'current', 'charge', 'voltage', 'capacitance', 'resistance',
|
|
44
|
+
'conductance', 'magnetic_flux', 'magnetic_flux_density', 'inductance',
|
|
45
|
+
// 光学
|
|
46
|
+
'solid_angle', 'luminous_intensity', 'luminous_flux', 'illuminance',
|
|
47
|
+
// 化学与核物理
|
|
48
|
+
'amount_of_substance', 'activity', 'absorbed_dose',
|
|
49
|
+
'dose_equivalent', 'catalytic_activity',
|
|
50
|
+
]
|
|
38
51
|
|
|
39
52
|
/**
|
|
40
53
|
* 把作者传来的自定义单位规范化。
|
|
@@ -144,8 +157,30 @@ export function opUnits(args: MathArgs): Outcome {
|
|
|
144
157
|
}
|
|
145
158
|
|
|
146
159
|
// ── 情形 A:列清单 ──
|
|
160
|
+
/**
|
|
161
|
+
* ## 为什么默认只给摘要
|
|
162
|
+
*
|
|
163
|
+
* 单位收到 287 条后,全量展开一次是 17000+ 字符 —— 那是**每轮对话都占上下文**的东西,
|
|
164
|
+
* 而调用方通常只想确认「有没有我要的那条」。
|
|
165
|
+
*
|
|
166
|
+
* 所以默认给「每个量纲几条约数 + 几个样例」,要看全表就传 `unitScale`
|
|
167
|
+
* (如 `unitScale:"length"`)。这与 help 的取向一致:**用到时才读**。
|
|
168
|
+
*/
|
|
147
169
|
if (!hasFrom && !hasTo) {
|
|
148
|
-
|
|
170
|
+
const wantedDimension = typeof args.unitScale === 'string' ? args.unitScale.trim().toLowerCase() : ''
|
|
171
|
+
const expandDimension =
|
|
172
|
+
wantedDimension === ''
|
|
173
|
+
? null
|
|
174
|
+
: ORDER.find(function (d) { return d === wantedDimension || DIMENSION_LABEL[d] === wantedDimension }) ?? null
|
|
175
|
+
if (wantedDimension !== '' && expandDimension === null) {
|
|
176
|
+
throw new CalcError(
|
|
177
|
+
'invalid_argument',
|
|
178
|
+
'不认识的量纲「' + wantedDimension + '」',
|
|
179
|
+
'可用: ' + ORDER.map(function (d) { return d + '(' + DIMENSION_LABEL[d] + ')' }).join(' / '),
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
lines.push('══════ 可用单位(共 ' + (UNITS.length + custom.length) + ' 条)══════')
|
|
149
184
|
lines.push('')
|
|
150
185
|
if (custom.length > 0) {
|
|
151
186
|
lines.push('【本项目的自定义单位】')
|
|
@@ -161,23 +196,62 @@ export function opUnits(args: MathArgs): Outcome {
|
|
|
161
196
|
for (const dimension of ORDER) {
|
|
162
197
|
const items = [...custom.filter(function (u) { return u.dimension === dimension }), ...UNITS.filter(function (u) { return u.dimension === dimension })]
|
|
163
198
|
if (items.length === 0) continue
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
199
|
+
/** 展开只针对点名的那一维;其余给计数与样例。 */
|
|
200
|
+
if (expandDimension === dimension) {
|
|
201
|
+
/**
|
|
202
|
+
* 展开时**按用途分两段**:度量衡在前(绝大多数调用者要这个),
|
|
203
|
+
* 科学单位在后并标明。混排会让「找三丈是多少米」的人翻过一堆 eV。
|
|
204
|
+
*/
|
|
205
|
+
const measureItems = items.filter(function (u) { return u.category !== 'science' })
|
|
206
|
+
const scienceItems = items.filter(function (u) { return u.category === 'science' })
|
|
167
207
|
lines.push(
|
|
168
|
-
'
|
|
169
|
-
|
|
208
|
+
'【' + DIMENSION_LABEL[dimension] + '】(基准单位 ' + BASE_UNIT[dimension] + ')' +
|
|
209
|
+
'共 ' + items.length + ' 条:度量衡 ' + measureItems.length + ' / 科学 ' + scienceItems.length,
|
|
210
|
+
)
|
|
211
|
+
for (const unit of measureItems) {
|
|
212
|
+
const aliases = (unit.aliases || []).length > 0 ? '(别名:' + unit.aliases!.join('、') + ')' : ''
|
|
213
|
+
lines.push(
|
|
214
|
+
' ' + unit.name.padEnd(12) + ' = ' + showFactor(unit.factor).padEnd(22) +
|
|
215
|
+
BASE_UNIT[dimension] + aliases,
|
|
216
|
+
)
|
|
217
|
+
if (unit.note) lines.push(' · ' + unit.note)
|
|
218
|
+
}
|
|
219
|
+
if (scienceItems.length > 0) {
|
|
220
|
+
lines.push(' ── 科学单位(专业计算用,正文里通常不写)──')
|
|
221
|
+
for (const unit of scienceItems) {
|
|
222
|
+
const aliases = (unit.aliases || []).length > 0 ? '(别名:' + unit.aliases!.join('、') + ')' : ''
|
|
223
|
+
lines.push(
|
|
224
|
+
' ' + unit.name.padEnd(12) + ' = ' + showFactor(unit.factor).padEnd(22) +
|
|
225
|
+
BASE_UNIT[dimension] + aliases,
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
lines.push('')
|
|
230
|
+
} else {
|
|
231
|
+
/** 摘要行也标出度量衡/科学的条数——不展开也能看出这条量纲的分界。 */
|
|
232
|
+
const measureCount = items.filter(function (u) { return u.category !== 'science' }).length
|
|
233
|
+
const scienceCount = items.length - measureCount
|
|
234
|
+
const sample = items.slice(0, 5).map(function (u) { return u.name }).join('/')
|
|
235
|
+
lines.push(
|
|
236
|
+
' ' + DIMENSION_LABEL[dimension].padEnd(4) + BASE_UNIT[dimension].padEnd(6) +
|
|
237
|
+
('度量衡 ' + measureCount).padEnd(9) +
|
|
238
|
+
('科学 ' + scienceCount).padEnd(7) + '例:' + sample + (items.length > 5 ? '/…' : ''),
|
|
170
239
|
)
|
|
171
|
-
if (unit.note) lines.push(' · ' + unit.note)
|
|
172
240
|
}
|
|
173
|
-
lines.push('')
|
|
174
241
|
}
|
|
242
|
+
if (expandDimension === null) {
|
|
243
|
+
lines.push('【看某一类的全部单位】')
|
|
244
|
+
lines.push(' 传 unitScale:"length" / "mass" / "volume" / …(也可用中文名如 "长度")')
|
|
245
|
+
}
|
|
246
|
+
lines.push('')
|
|
175
247
|
lines.push('【怎么换算】')
|
|
176
248
|
lines.push(' {operation:"units", unitValue:30, unitFrom:"里", unitTo:"km"}')
|
|
177
249
|
lines.push(' → 30 里 = 15 km')
|
|
178
250
|
lines.push('')
|
|
179
251
|
lines.push('注:中文古制单位历朝不同,内置值取最常见近似;要精确到某朝代,')
|
|
180
252
|
lines.push(' 用 customUnits 覆盖(如 {name:"里",dimension:"length",factor:576})。')
|
|
253
|
+
lines.push(' 同一名字在两套体系里数值不同的(加仑、担、石、马力),')
|
|
254
|
+
lines.push(' 已分两类收(gal/gal_uk、hp/hp_metric),按 note 区分。')
|
|
181
255
|
return {
|
|
182
256
|
result: lines.join('\n'),
|
|
183
257
|
value: toJsonValue({
|
|
@@ -228,6 +302,48 @@ export function opUnits(args: MathArgs): Outcome {
|
|
|
228
302
|
}
|
|
229
303
|
const c = outcome.conversion
|
|
230
304
|
|
|
305
|
+
/**
|
|
306
|
+
* `terse`:只回「A = B」一行。
|
|
307
|
+
*
|
|
308
|
+
* ## 为什么让 terse 对 units 也生效
|
|
309
|
+
*
|
|
310
|
+
* 实测反馈(2026-11-06):`{operation:"units", terse:true}` 返回仍是 6 行带框,
|
|
311
|
+
* terse 对 units **没有任何压缩效果** —— 而 units 的输出本来就有
|
|
312
|
+
* 「代入过程 + 注意」两段可省的内容。
|
|
313
|
+
*
|
|
314
|
+
* ## 但**注意**那一段不能省,只压缩格式
|
|
315
|
+
*
|
|
316
|
+
* 与批量一致:terse 承诺的是「省掉标题与统计」,不是省掉风险。
|
|
317
|
+
* 「里在周制约 415 m」这类适用域提示如果被省掉,
|
|
318
|
+
* 调用方会拿一个用错朝代的值去写设定 —— 那比多几行更糟。
|
|
319
|
+
* 所以 terse 下保留内容,只去掉竖排与空行。
|
|
320
|
+
*/
|
|
321
|
+
if (args.terse === true) {
|
|
322
|
+
const terseLines: string[] = []
|
|
323
|
+
terseLines.push(show(c.value) + ' ' + c.from.name + ' = ' + show(c.result) + ' ' + c.to.name)
|
|
324
|
+
if (implicitOne) terseLines[0] = '(未给 unitValue,按 1 计)' + terseLines[0]
|
|
325
|
+
/**
|
|
326
|
+
* 适用域提示合并成一行 —— 它们在 terse 下仍必须出现,
|
|
327
|
+
* 只是不再逐条占一行。
|
|
328
|
+
*/
|
|
329
|
+
if (c.warnings.length > 0) terseLines.push('⚠ ' + c.warnings.join(';'))
|
|
330
|
+
return {
|
|
331
|
+
result: terseLines.join('\n'),
|
|
332
|
+
value: toJsonValue({
|
|
333
|
+
mode: 'convert',
|
|
334
|
+
dimension: c.dimension,
|
|
335
|
+
value: safeNumberForJson(c.value),
|
|
336
|
+
from: c.from.name,
|
|
337
|
+
to: c.to.name,
|
|
338
|
+
result: safeNumberForJson(c.result),
|
|
339
|
+
steps: c.steps,
|
|
340
|
+
warnings: c.warnings,
|
|
341
|
+
implicitOne: implicitOne,
|
|
342
|
+
terse: true,
|
|
343
|
+
} as never),
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
231
347
|
lines.push('══════ 单位换算 ══════')
|
|
232
348
|
lines.push('')
|
|
233
349
|
if (implicitOne) {
|
package/src/ops/workspace-op.ts
CHANGED
|
@@ -213,6 +213,16 @@ export function opWorkspace(args: MathArgs): Outcome {
|
|
|
213
213
|
next.variables = { ...next.variables, ...args.workspaceVariables }
|
|
214
214
|
}
|
|
215
215
|
if (args.description) next.description = args.description
|
|
216
|
+
/**
|
|
217
|
+
* 命名变量是否参与计算(默认关闭)。
|
|
218
|
+
*
|
|
219
|
+
* 单独一个开关而不是「存了就用」,是为了让这条状态变化是**显式的一次动作**:
|
|
220
|
+
* 打开它会让同一个调用在不同工作区状态下给出不同结果,
|
|
221
|
+
* 调用方应当知道自己在换掉什么。
|
|
222
|
+
*/
|
|
223
|
+
if (args.useWorkspaceVariables !== undefined) {
|
|
224
|
+
next.useWorkspaceVariables = Boolean(args.useWorkspaceVariables)
|
|
225
|
+
}
|
|
216
226
|
/**
|
|
217
227
|
* 显示精度固化。
|
|
218
228
|
*
|
package/src/playbook.ts
CHANGED
|
@@ -295,18 +295,43 @@ export function buildPlaybook(state: {
|
|
|
295
295
|
* @param state - 工作区状态
|
|
296
296
|
* @returns 精简提示词
|
|
297
297
|
*/
|
|
298
|
+
/**
|
|
299
|
+
* brief 档的指令集(每轮都随注入下发,**每个字都在付费**)。
|
|
300
|
+
*
|
|
301
|
+
* ## 字数按「使用频率 × 重要程度」分配
|
|
302
|
+
*
|
|
303
|
+
* 曾经的问题是比例失调:整段 817 字符里,「世界推演」占了 360(44%),
|
|
304
|
+
* 而它只对做架空世界设定那类会话有用 —— 算术、单位换算、统计这些
|
|
305
|
+
* **高频场景完全用不到**,却要为它每轮付 360 字符。
|
|
306
|
+
*
|
|
307
|
+
* 现在按三档分配:
|
|
308
|
+
*
|
|
309
|
+
* | 内容 | 频率 | 重要度 | 字数 |
|
|
310
|
+
* |---|---|---|---|
|
|
311
|
+
* | 行为约束(必须调用 / 不要心算) | 每轮 | 极高 | ~40 |
|
|
312
|
+
* | 该用哪个 operation(最常问) | 极高 | 高 | ~70 |
|
|
313
|
+
* | 可信度标签怎么读 | 中 | 极高 | ~60 |
|
|
314
|
+
* | 精度两层 | 中 | 高 | ~50 |
|
|
315
|
+
* | 世界推演分工(低频,压到极限) | 低 | 中 | ~80 |
|
|
316
|
+
*
|
|
317
|
+
* ## 顺手修掉两处自相矛盾
|
|
318
|
+
*
|
|
319
|
+
* 原第 5 条说「输出默认全精度(几十位小数)」、第 6 条说「默认 8 位小数」——
|
|
320
|
+
* 两条并列下发,模型读到的是互相冲突的指令。**8 位才是现状**(2.1.0 起),
|
|
321
|
+
* 「全精度」那句是过时表述,已删。
|
|
322
|
+
*
|
|
323
|
+
* @param state - 工作区状态(改过的常数、自创量)
|
|
324
|
+
* @returns 指令集文本
|
|
325
|
+
*/
|
|
298
326
|
export function buildBriefPlaybook(state: {
|
|
299
327
|
changedConstants?: string[]
|
|
300
328
|
customConstants?: number
|
|
301
329
|
}): string {
|
|
302
330
|
const out: string[] = []
|
|
303
|
-
|
|
304
|
-
out.push('
|
|
305
|
-
out.push('-
|
|
306
|
-
out.push('-
|
|
307
|
-
out.push('- 算不出来的别编,标待定或问用户')
|
|
308
|
-
out.push('- 工具输出默认是**全精度**(可能几十位小数),是供你取舍的原料。**保留几位你来定**:读者只要感觉就少给,要核对就多给,要接着算就管够。别把 60 位抄给读者,也别把该精确的整数(如 10^20+5)截成科学计数法')
|
|
309
|
-
out.push('- 工具输出默认按**小数点后 8 位**显示(整数永远完整,如 10^20+5 不会被截)。需要更多位就单次传 `displayPrecision`;想全局换一档用 `{operation:"workspace", action2:"set", displayPrecision:N}` 固化一次。注意 `precision`(计算精度)不要调低,它一低大数就会算错')
|
|
331
|
+
// 低频内容压到 120 字符内。四句话的语义一个不少,只是去掉重复的修饰。
|
|
332
|
+
out.push('**世界推演:底座算数,你判断表达。**')
|
|
333
|
+
out.push('- 算:`derivation`(pipeline/backSolve/dead/observer/chaos);写:结语叙事你生成')
|
|
334
|
+
out.push('- 硬约束如实说;叙事选择不反驳只讲代价;算不出的别编,标待定')
|
|
310
335
|
if ((state.changedConstants ?? []).length > 0) {
|
|
311
336
|
out.push('- 本区改过常数,先跑 dimensionless 确认不是单位重定义')
|
|
312
337
|
}
|