@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24
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 +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +16 -9
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +547 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- package/ts/src/unified.ts +316 -54
|
@@ -1,57 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* issue #77 E2E:LLM_MODEL 对 dsh 会话面生效的端到端证据。
|
|
3
|
+
*
|
|
4
|
+
* 四场景(全部走真实路径:clean package bin → dsh headless →
|
|
5
|
+
* llm-deepseek → 本地 mock OpenAI 兼容中转,中转落盘每个请求体的 model 字段):
|
|
6
|
+
*
|
|
7
|
+
* 1. 设 LLM_MODEL,干净 DSH_HOME → 请求体 model = LLM_MODEL
|
|
8
|
+
* 2. 设 LLM_MODEL,DSH_HOME 预置用户层选择 → 请求体 model = LLM_MODEL
|
|
9
|
+
* (用户层 ~/.dsh 选择被 .env 显式意图压过——#77 发现场景的正面回归)
|
|
10
|
+
* 3. 不设 LLM_MODEL,干净 DSH_HOME → 请求体 model = deepseek-v4-flash
|
|
11
|
+
* (dsh 组合默认,默认路径面不变)
|
|
12
|
+
* 4. 不设 LLM_MODEL,DSH_HOME 预置用户层选择 → 请求体 model = 用户层模型
|
|
13
|
+
* (用户 web 选择不被误伤,默认路径面不变)
|
|
14
|
+
*
|
|
15
|
+
* 隔离纪律(巡检/测试状态纪律):DSH_HOME 指向临时目录(会话/设置零污染);
|
|
16
|
+
* mock 模型只回固定文本、从不调工具 → gotry-state 零写入(动机/愿望池不动);
|
|
17
|
+
* 环境变量直接 export(不走 .env 文件,repo 模式 .env 加载路径已由 #48 覆盖)。
|
|
18
|
+
*
|
|
19
|
+
* 形态:只接受 clean npm 安装形态——插件走 dist/ 纯 JS,不再通过移动源码树依赖
|
|
20
|
+
* 来伪装 package mode。调用方须提供当前 tarball 安装出的 bin。
|
|
21
|
+
*
|
|
22
|
+
* 运行(在仓根):GOTRY_MODEL_OVERRIDE_E2E_BIN=/clean/node_modules/.bin/gotry
|
|
23
|
+
* npx tsx ts/scripts/model-override-e2e.ts
|
|
24
|
+
*/
|
|
1
25
|
import { createServer } from 'node:http';
|
|
2
26
|
import { existsSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
3
27
|
import { tmpdir } from 'node:os';
|
|
4
28
|
import { join } from 'node:path';
|
|
5
29
|
import { spawn } from 'node:child_process';
|
|
6
30
|
const BOOT_TIMEOUT_MS = 120_000;
|
|
31
|
+
/** mock OpenAI 兼容中转:落盘请求体 model,回最小合法 SSE 流 */
|
|
7
32
|
async function startMockRelay() {
|
|
8
33
|
const bodies = [];
|
|
9
|
-
const server = createServer((req, res)=>{
|
|
34
|
+
const server = createServer((req, res) => {
|
|
10
35
|
if (req.method !== 'POST' || !req.url?.endsWith('/chat/completions')) {
|
|
11
36
|
res.writeHead(404).end('not found');
|
|
12
37
|
return;
|
|
13
38
|
}
|
|
14
39
|
const chunks = [];
|
|
15
|
-
req.on('data', (c)=>chunks.push(c));
|
|
16
|
-
req.on('end', ()=>{
|
|
40
|
+
req.on('data', (c) => chunks.push(c));
|
|
41
|
+
req.on('end', () => {
|
|
17
42
|
try {
|
|
18
43
|
bodies.push(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
|
|
19
|
-
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
20
46
|
bodies.push({});
|
|
21
47
|
}
|
|
22
|
-
const chunk = (delta, finish)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
created: 1,
|
|
26
|
-
model: 'mock',
|
|
27
|
-
choices: [
|
|
28
|
-
{
|
|
29
|
-
index: 0,
|
|
30
|
-
delta,
|
|
31
|
-
finish_reason: finish
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
})}\n\n`;
|
|
35
|
-
res.writeHead(200, {
|
|
36
|
-
'content-type': 'text/event-stream',
|
|
37
|
-
'cache-control': 'no-cache'
|
|
38
|
-
});
|
|
39
|
-
res.write(chunk({
|
|
40
|
-
role: 'assistant',
|
|
41
|
-
content: 'ok'
|
|
42
|
-
}, null));
|
|
48
|
+
const chunk = (delta, finish) => `data: ${JSON.stringify({ id: 'e2e', object: 'chat.completion.chunk', created: 1, model: 'mock', choices: [{ index: 0, delta, finish_reason: finish }] })}\n\n`;
|
|
49
|
+
res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' });
|
|
50
|
+
res.write(chunk({ role: 'assistant', content: 'ok' }, null));
|
|
43
51
|
res.write(chunk({}, 'stop'));
|
|
44
52
|
res.write('data: [DONE]\n\n');
|
|
45
53
|
res.end();
|
|
46
54
|
});
|
|
47
55
|
});
|
|
48
|
-
await new Promise((resolve)=>server.listen(0, '127.0.0.1', resolve));
|
|
56
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
49
57
|
const port = server.address().port;
|
|
50
|
-
return {
|
|
51
|
-
port,
|
|
52
|
-
bodies,
|
|
53
|
-
close: ()=>new Promise((resolve)=>server.close(()=>resolve()))
|
|
54
|
-
};
|
|
58
|
+
return { port, bodies, close: () => new Promise((resolve) => server.close(() => resolve())) };
|
|
55
59
|
}
|
|
56
60
|
async function runScenario(s, packageBin) {
|
|
57
61
|
const relay = await startMockRelay();
|
|
@@ -59,70 +63,45 @@ async function runScenario(s, packageBin) {
|
|
|
59
63
|
const childCwd = mkdtempSync(join(tmpdir(), 'gotry-77-cwd-'));
|
|
60
64
|
try {
|
|
61
65
|
if (s.userLayerModel) {
|
|
66
|
+
// 预置用户层设置(等价于用户在 dsh web UI 选过模型并持久化)
|
|
62
67
|
writeFileSync(join(dshHome, 'settings.yaml'), `agent-default-model:\n provider: deepseek-official\n model: ${s.userLayerModel}\n`);
|
|
63
68
|
}
|
|
64
69
|
const env = {
|
|
65
70
|
...process.env,
|
|
66
71
|
LLM_API_KEY: 'e2e-mock-key',
|
|
67
72
|
LLM_BASE_URL: `http://127.0.0.1:${relay.port}/v1`,
|
|
68
|
-
DSH_HOME: dshHome
|
|
73
|
+
DSH_HOME: dshHome,
|
|
69
74
|
};
|
|
70
|
-
delete env['GOTRY_LLM_MODEL'];
|
|
71
|
-
if (s.llmModel)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
], {
|
|
75
|
+
delete env['GOTRY_LLM_MODEL']; // 防宿主环境串扰;只认本场景显式设定
|
|
76
|
+
if (s.llmModel)
|
|
77
|
+
env['LLM_MODEL'] = s.llmModel;
|
|
78
|
+
else
|
|
79
|
+
delete env['LLM_MODEL'];
|
|
80
|
+
const child = spawn(packageBin, ['reply with the word ok'], {
|
|
76
81
|
env,
|
|
77
82
|
cwd: childCwd,
|
|
78
|
-
stdio: [
|
|
79
|
-
'ignore',
|
|
80
|
-
'pipe',
|
|
81
|
-
'pipe'
|
|
82
|
-
]
|
|
83
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
83
84
|
});
|
|
84
85
|
let logTail = '';
|
|
85
|
-
child.stdout.on('data', (d)=>{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
91
|
-
const exit = await new Promise((resolve)=>{
|
|
92
|
-
const timer = setTimeout(()=>{
|
|
93
|
-
child.kill('SIGKILL');
|
|
94
|
-
resolve(null);
|
|
95
|
-
}, BOOT_TIMEOUT_MS);
|
|
96
|
-
child.on('exit', (code)=>{
|
|
97
|
-
clearTimeout(timer);
|
|
98
|
-
resolve(code);
|
|
99
|
-
});
|
|
86
|
+
child.stdout.on('data', (d) => { logTail = (logTail + d).slice(-2000); });
|
|
87
|
+
child.stderr.on('data', (d) => { logTail = (logTail + d).slice(-2000); });
|
|
88
|
+
const exit = await new Promise((resolve) => {
|
|
89
|
+
const timer = setTimeout(() => { child.kill('SIGKILL'); resolve(null); }, BOOT_TIMEOUT_MS);
|
|
90
|
+
child.on('exit', (code) => { clearTimeout(timer); resolve(code); });
|
|
100
91
|
});
|
|
101
92
|
if (relay.bodies.length === 0) {
|
|
102
|
-
return {
|
|
103
|
-
ok: false,
|
|
104
|
-
detail: `中转未收到任何请求(exit=${exit});尾部日志: ${logTail.slice(-400)}`
|
|
105
|
-
};
|
|
93
|
+
return { ok: false, detail: `中转未收到任何请求(exit=${exit});尾部日志: ${logTail.slice(-400)}` };
|
|
106
94
|
}
|
|
107
|
-
const models = relay.bodies.map((b)=>String(b.model));
|
|
108
|
-
const bad = models.filter((m)=>m !== s.expectModel);
|
|
109
|
-
return bad.length === 0
|
|
110
|
-
ok: true,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
detail: `请求体 model 不符:期望 ${s.expectModel},实际 ${JSON.stringify(models)}`
|
|
115
|
-
};
|
|
116
|
-
} finally{
|
|
95
|
+
const models = relay.bodies.map((b) => String(b.model));
|
|
96
|
+
const bad = models.filter((m) => m !== s.expectModel);
|
|
97
|
+
return bad.length === 0
|
|
98
|
+
? { ok: true, detail: `${models.length} 个请求体 model 全部 = ${s.expectModel}(exit=${exit})` }
|
|
99
|
+
: { ok: false, detail: `请求体 model 不符:期望 ${s.expectModel},实际 ${JSON.stringify(models)}` };
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
117
102
|
await relay.close();
|
|
118
|
-
rmSync(dshHome, {
|
|
119
|
-
|
|
120
|
-
force: true
|
|
121
|
-
});
|
|
122
|
-
rmSync(childCwd, {
|
|
123
|
-
recursive: true,
|
|
124
|
-
force: true
|
|
125
|
-
});
|
|
103
|
+
rmSync(dshHome, { recursive: true, force: true });
|
|
104
|
+
rmSync(childCwd, { recursive: true, force: true });
|
|
126
105
|
}
|
|
127
106
|
}
|
|
128
107
|
async function main() {
|
|
@@ -135,34 +114,19 @@ async function main() {
|
|
|
135
114
|
}
|
|
136
115
|
async function runAll(packageBin) {
|
|
137
116
|
const scenarios = [
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: '② 设 LLM_MODEL + 用户层已选模型',
|
|
145
|
-
llmModel: 'gotry-e2e-model',
|
|
146
|
-
userLayerModel: 'user-picked-model',
|
|
147
|
-
expectModel: 'gotry-e2e-model'
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: '③ 不设 LLM_MODEL(干净 DSH_HOME)',
|
|
151
|
-
expectModel: 'deepseek-v4-flash'
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
name: '④ 不设 LLM_MODEL + 用户层已选模型',
|
|
155
|
-
userLayerModel: 'user-picked-model',
|
|
156
|
-
expectModel: 'user-picked-model'
|
|
157
|
-
}
|
|
117
|
+
{ name: '① 设 LLM_MODEL(干净 DSH_HOME)', llmModel: 'gotry-e2e-model', expectModel: 'gotry-e2e-model' },
|
|
118
|
+
{ name: '② 设 LLM_MODEL + 用户层已选模型', llmModel: 'gotry-e2e-model', userLayerModel: 'user-picked-model', expectModel: 'gotry-e2e-model' },
|
|
119
|
+
{ name: '③ 不设 LLM_MODEL(干净 DSH_HOME)', expectModel: 'deepseek-v4-flash' },
|
|
120
|
+
{ name: '④ 不设 LLM_MODEL + 用户层已选模型', userLayerModel: 'user-picked-model', expectModel: 'user-picked-model' },
|
|
158
121
|
];
|
|
159
122
|
let fail = 0;
|
|
160
|
-
for (const s of scenarios){
|
|
123
|
+
for (const s of scenarios) {
|
|
161
124
|
process.stdout.write(`${s.name} … `);
|
|
162
125
|
const t0 = Date.now();
|
|
163
126
|
const r = await runScenario(s, packageBin);
|
|
164
127
|
console.log(`${r.ok ? 'PASS' : 'FAIL'}(${((Date.now() - t0) / 1000).toFixed(1)}s)${r.detail}`);
|
|
165
|
-
if (!r.ok)
|
|
128
|
+
if (!r.ok)
|
|
129
|
+
fail = 1;
|
|
166
130
|
}
|
|
167
131
|
if (fail) {
|
|
168
132
|
console.error('\nMODEL OVERRIDE E2E FAIL');
|
|
@@ -171,6 +135,3 @@ async function runAll(packageBin) {
|
|
|
171
135
|
console.log('\nMODEL OVERRIDE E2E OK');
|
|
172
136
|
}
|
|
173
137
|
await main();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
//# sourceURL=ts/scripts/model-override-e2e.ts
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M3 nightly 证据生产器合同测试(run-all §35,issue #22 验收⑥「nightly real-LLM 与成本质量证据可复跑」)。
|
|
3
|
+
* 全确定性、零网络、零 API 花费:
|
|
4
|
+
* - 封存价表仅可审计换算(peak 保守上界),未知模型 fail-closed,usage 缺失 fail-closed;
|
|
5
|
+
* - run_key 确定性 + 记录必须过消费方 parseNightlyRun(生产器写的就是评分器读的);
|
|
6
|
+
* - 无凭证 = waiting_external_evidence,不写任何文件(issue #22 停机纪律);
|
|
7
|
+
* - --dry-run mock 全链演练可跑通且绝不落盘。
|
|
8
|
+
* 真跑(花钱)不在 CI:heartbeat/founder 手动 `npx tsx scripts/nightly-evidence.ts`。
|
|
9
|
+
*
|
|
10
|
+
* 价表 schema 演进(issue #49):v1 (legacy DeepSeek only) / v2 (provider-aware;DeepSeek+MiniMax);
|
|
11
|
+
* 本测试在 v2 形态下覆盖:DeepSeek alias、裸模型名、MiniMax 跨 provider 命中、跨 provider 守门、
|
|
12
|
+
* MiniMax flat_no_offpeak 纪律同价守门、未声明 schema_version 拒绝、unknown model fail-closed、
|
|
13
|
+
* usage 缺失 fail-closed、run_key 确定性 + 消费方 parseNightlyRun、无凭证 waiting、dry-run 零落盘。
|
|
14
|
+
*/
|
|
1
15
|
import assert from 'node:assert/strict';
|
|
2
16
|
import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
17
|
import { join } from 'node:path';
|
|
4
18
|
import { tmpdir } from 'node:os';
|
|
5
|
-
import { buildNightlyRunRecord, loadPriceTable, loadPromptSet, priceRunCost, runNightlyEvidence } from './nightly-evidence.js';
|
|
19
|
+
import { buildNightlyRunRecord, loadPriceTable, loadPromptSet, priceRunCost, runNightlyEvidence, } from './nightly-evidence.js';
|
|
6
20
|
import { parseNightlyRun } from './product-metrics.js';
|
|
7
21
|
let passed = 0;
|
|
8
22
|
function ok(label) {
|
|
9
23
|
passed += 1;
|
|
10
24
|
console.log(` ok ${label}`);
|
|
11
25
|
}
|
|
26
|
+
// ---- 1. 封存文件可加载,schema v2 + provider-aware alias/裸模型双查找(以仓库内真正随发的数据文件为夹具) ----
|
|
12
27
|
const priceTable = loadPriceTable('data/llm-price-table.json');
|
|
13
28
|
const promptSet = loadPromptSet('data/m3-nightly-prompts.json');
|
|
14
29
|
assert.equal(priceTable.schema_version, 'gotry_llm_price_table_v2');
|
|
@@ -20,100 +35,76 @@ assert.ok(priceTable.prices['minimax:MiniMax-M3'], 'qualified 键 minimax:MiniMa
|
|
|
20
35
|
assert.ok(priceTable.aliases['deepseek-chat'], 'deepseek-chat 必须在 aliases 表');
|
|
21
36
|
assert.equal(priceTable.aliases['deepseek-chat'], 'deepseek-v4-flash');
|
|
22
37
|
ok('v2 schema 载入 + 裸键 + qualified 键 + alias 双查找');
|
|
23
|
-
|
|
38
|
+
// ---- 2. 保守上界不变量:每个价目 hit<=miss 且 offpeak<=peak(只高不低) ----
|
|
39
|
+
for (const [name, entry] of Object.entries(priceTable.prices)) {
|
|
24
40
|
assert.ok(entry.input_cache_hit_usd_per_1m_peak <= entry.input_cache_miss_usd_per_1m_peak, `${name} hit<=miss`);
|
|
25
41
|
assert.ok(entry.output_usd_per_1m_offpeak <= entry.output_usd_per_1m_peak, `${name} offpeak<=peak`);
|
|
26
42
|
}
|
|
27
43
|
ok('价目保守上界不变量(hit<=miss, offpeak<=peak)');
|
|
44
|
+
// ---- 3. 换算数学:deepseek-chat -> v4-flash peak(miss 1M + 1M out = 1.76) ----
|
|
28
45
|
const baseUsage = {
|
|
29
46
|
calls: 3,
|
|
30
47
|
inputTokens: 1_000_000,
|
|
31
48
|
outputTokens: 1_000_000,
|
|
32
49
|
inputCacheHitTokens: 0,
|
|
33
50
|
inputCacheMissTokens: 1_000_000,
|
|
34
|
-
responsesMissingUsage: 0
|
|
51
|
+
responsesMissingUsage: 0,
|
|
35
52
|
};
|
|
36
53
|
const cost = priceRunCost('deepseek-chat', baseUsage, priceTable);
|
|
37
|
-
assert.equal(cost, 1.76);
|
|
54
|
+
assert.equal(cost, 1.76); // 0.44 + 1.32
|
|
38
55
|
ok('peak miss 换算 0.44+1.32=1.76(经 alias)');
|
|
39
|
-
const hitUsage = {
|
|
40
|
-
|
|
41
|
-
inputCacheHitTokens: 1_000_000,
|
|
42
|
-
inputCacheMissTokens: 0
|
|
43
|
-
};
|
|
44
|
-
assert.equal(priceRunCost('deepseek-chat', hitUsage, priceTable), 1.334);
|
|
56
|
+
const hitUsage = { ...baseUsage, inputCacheHitTokens: 1_000_000, inputCacheMissTokens: 0 };
|
|
57
|
+
assert.equal(priceRunCost('deepseek-chat', hitUsage, priceTable), 1.334); // 0.014+1.32
|
|
45
58
|
ok('peak hit 换算 0.014+1.32=1.334');
|
|
59
|
+
// ---- 3b. MiniMax-M3 peak 换算(issue #49 验收):miss 1M + 1M out = 3.00(0.60+2.40,ADR-11 保守上界) ----
|
|
46
60
|
const m3cost = priceRunCost('MiniMax-M3', baseUsage, priceTable);
|
|
47
|
-
assert.equal(m3cost, 3.00);
|
|
61
|
+
assert.equal(m3cost, 3.00); // 0.60 + 2.40
|
|
48
62
|
ok('MiniMax-M3 peak miss 换算 0.60+2.40=3.00');
|
|
49
63
|
const m3hit = priceRunCost('MiniMax-M3', hitUsage, priceTable);
|
|
50
|
-
assert.equal(m3hit, 2.52);
|
|
64
|
+
assert.equal(m3hit, 2.52); // 0.12+2.40
|
|
51
65
|
ok('MiniMax-M3 peak hit 换算 0.12+2.40=2.52');
|
|
66
|
+
// ---- 3c. MiniMax flat_no_offpeak 纪律:peak 与 offpeak 列同价 —— 给定用量下两种策略产出同一价格 ----
|
|
52
67
|
const m3prov = priceTable.prices['minimax:MiniMax-M3'];
|
|
53
68
|
assert.equal(m3prov.input_cache_miss_usd_per_1m_peak, m3prov.input_cache_miss_usd_per_1m_offpeak, 'MiniMax-M3 flat 纪律:peak==offpeak miss');
|
|
54
69
|
assert.equal(m3prov.input_cache_hit_usd_per_1m_peak, m3prov.input_cache_hit_usd_per_1m_offpeak, 'MiniMax-M3 flat 纪律:peak==offpeak hit');
|
|
55
70
|
assert.equal(m3prov.output_usd_per_1m_peak, m3prov.output_usd_per_1m_offpeak, 'MiniMax-M3 flat 纪律:peak==offpeak output');
|
|
56
71
|
ok('MiniMax flat_no_offpeak 纪律同价守门');
|
|
57
|
-
|
|
72
|
+
// ---- 3d. issue #49 验收:人为改一个不存在的模型名 → 仍 fail-closed(waiting/error,不猜价) ----
|
|
73
|
+
assert.throws(() => priceRunCost('unknown-model-xyz', baseUsage, priceTable), /no entry for model/);
|
|
58
74
|
ok('未知模型名 → fail-closed');
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
-
assert.throws(()=>priceRunCost('deepseek-chat', baseUsage, dangling), /has no entry/);
|
|
66
|
-
assert.throws(()=>priceRunCost('deepseek-chat', {
|
|
67
|
-
...baseUsage,
|
|
68
|
-
responsesMissingUsage: 1
|
|
69
|
-
}, priceTable), /cost unprovable/);
|
|
75
|
+
// ---- 4. fail-closed:未知模型 / alias 悬空 / usage 缺失 / 非法 schema_version 都不许出现价格 ----
|
|
76
|
+
const dangling = { ...priceTable, aliases: { 'deepseek-chat': 'not-a-model' } };
|
|
77
|
+
assert.throws(() => priceRunCost('deepseek-chat', baseUsage, dangling), /has no entry/);
|
|
78
|
+
assert.throws(() => priceRunCost('deepseek-chat', { ...baseUsage, responsesMissingUsage: 1 }, priceTable), /cost unprovable/);
|
|
79
|
+
// 非法 schema_version 守门(写入 tmp 后用 loadPriceTable 加载应抛错)
|
|
70
80
|
const tmpSchemaDir = mkdtempSync(join(tmpdir(), 'nightly-bad-schema-'));
|
|
71
81
|
const badSchemaPath = join(tmpSchemaDir, 'bad.json');
|
|
72
82
|
try {
|
|
73
|
-
writeFileSync(badSchemaPath, JSON.stringify({
|
|
74
|
-
|
|
75
|
-
prices: {}
|
|
76
|
-
}));
|
|
77
|
-
assert.throws(()=>loadPriceTable(badSchemaPath), /schema_version must be/);
|
|
83
|
+
writeFileSync(badSchemaPath, JSON.stringify({ schema_version: 'gotry_llm_price_table_v99', prices: {} }));
|
|
84
|
+
assert.throws(() => loadPriceTable(badSchemaPath), /schema_version must be/);
|
|
78
85
|
ok('非法 schema_version 拒绝');
|
|
79
|
-
} finally{
|
|
80
|
-
rmSync(tmpSchemaDir, {
|
|
81
|
-
recursive: true,
|
|
82
|
-
force: true
|
|
83
|
-
});
|
|
84
86
|
}
|
|
87
|
+
finally {
|
|
88
|
+
rmSync(tmpSchemaDir, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
// 非法 provider price_strategy 守门(在 v2 表写入非法策略应抛错)
|
|
85
91
|
const tmpStrategyDir = mkdtempSync(join(tmpdir(), 'nightly-bad-strategy-'));
|
|
86
92
|
const badStrategyPath = join(tmpStrategyDir, 'bad.json');
|
|
87
93
|
try {
|
|
88
94
|
writeFileSync(badStrategyPath, JSON.stringify({
|
|
89
95
|
schema_version: 'gotry_llm_price_table_v2',
|
|
90
96
|
providers: {
|
|
91
|
-
bad: {
|
|
92
|
-
|
|
93
|
-
price_strategy: 'mystery_strategy',
|
|
94
|
-
aliases: {},
|
|
95
|
-
models: {
|
|
96
|
-
'm': {
|
|
97
|
-
input_cache_miss_usd_per_1m_peak: 0.1,
|
|
98
|
-
input_cache_hit_usd_per_1m_peak: 0.01,
|
|
99
|
-
output_usd_per_1m_peak: 0.5,
|
|
100
|
-
input_cache_miss_usd_per_1m_offpeak: 0.1,
|
|
101
|
-
input_cache_hit_usd_per_1m_offpeak: 0.01,
|
|
102
|
-
output_usd_per_1m_offpeak: 0.5
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
97
|
+
bad: { family: 'x', price_strategy: 'mystery_strategy', aliases: {}, models: { 'm': { input_cache_miss_usd_per_1m_peak: 0.1, input_cache_hit_usd_per_1m_peak: 0.01, output_usd_per_1m_peak: 0.5, input_cache_miss_usd_per_1m_offpeak: 0.1, input_cache_hit_usd_per_1m_offpeak: 0.01, output_usd_per_1m_offpeak: 0.5 } } },
|
|
98
|
+
},
|
|
107
99
|
}));
|
|
108
|
-
assert.throws(()=>loadPriceTable(badStrategyPath), /price_strategy must be/);
|
|
100
|
+
assert.throws(() => loadPriceTable(badStrategyPath), /price_strategy must be/);
|
|
109
101
|
ok('v2 非法 price_strategy 拒绝');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
force: true
|
|
114
|
-
});
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
rmSync(tmpStrategyDir, { recursive: true, force: true });
|
|
115
105
|
}
|
|
116
106
|
ok('未知模型/悬空alias/usage缺失 fail-closed');
|
|
107
|
+
// ---- 5. run_key 确定性 + 消费方合同 ----
|
|
117
108
|
const executeAt = '2026-08-29T12:00:00.000Z';
|
|
118
109
|
const recordInput = {
|
|
119
110
|
schema_version: 'gotry_m3_nightly_run_v1',
|
|
@@ -121,21 +112,17 @@ const recordInput = {
|
|
|
121
112
|
real_llm: true,
|
|
122
113
|
prompt_set_sha256: 'a'.repeat(64),
|
|
123
114
|
output_sha256: 'b'.repeat(64),
|
|
124
|
-
cost_usd: 0.25
|
|
115
|
+
cost_usd: 0.25,
|
|
125
116
|
};
|
|
126
117
|
const rec1 = buildNightlyRunRecord(recordInput);
|
|
127
|
-
const rec2 = buildNightlyRunRecord({
|
|
128
|
-
...recordInput
|
|
129
|
-
});
|
|
118
|
+
const rec2 = buildNightlyRunRecord({ ...recordInput });
|
|
130
119
|
assert.equal(rec1.run_key, rec2.run_key);
|
|
131
120
|
assert.match(rec1.run_key, /^hmac-sha256:[0-9a-f]{64}$/);
|
|
132
121
|
parseNightlyRun(JSON.parse(JSON.stringify(rec1)), 0);
|
|
133
|
-
const rec3 = buildNightlyRunRecord({
|
|
134
|
-
...recordInput,
|
|
135
|
-
cost_usd: 0.26
|
|
136
|
-
});
|
|
122
|
+
const rec3 = buildNightlyRunRecord({ ...recordInput, cost_usd: 0.26 });
|
|
137
123
|
assert.notEqual(rec3.run_key, rec1.run_key);
|
|
138
124
|
ok('run_key 确定性 + 已过消费方 parseNightlyRun');
|
|
125
|
+
// ---- 6. 无凭证 = waiting_external_evidence,零写入(evidence root 自身也不得被创建) ----
|
|
139
126
|
const prevKey = process.env['LLM_API_KEY'];
|
|
140
127
|
const prevDeepseekKey = process.env['DEEPSEEK_API_KEY'];
|
|
141
128
|
const waitParent = mkdtempSync(join(tmpdir(), 'nightly-wait-'));
|
|
@@ -143,44 +130,32 @@ const waitRoot = join(waitParent, 'evidence', 'm3');
|
|
|
143
130
|
try {
|
|
144
131
|
delete process.env['LLM_API_KEY'];
|
|
145
132
|
delete process.env['DEEPSEEK_API_KEY'];
|
|
146
|
-
const waiting = await runNightlyEvidence({
|
|
147
|
-
evidenceRoot: waitRoot,
|
|
148
|
-
dryRun: false,
|
|
149
|
-
clock: ()=>new Date(executeAt)
|
|
150
|
-
});
|
|
133
|
+
const waiting = await runNightlyEvidence({ evidenceRoot: waitRoot, dryRun: false, clock: () => new Date(executeAt) });
|
|
151
134
|
assert.equal(waiting.state, 'waiting_external_evidence');
|
|
152
135
|
assert.equal(waiting.record, null);
|
|
153
136
|
assert.ok(waiting.reason?.includes('backoff'));
|
|
154
137
|
assert.equal(existsSync(waitRoot), false, 'waiting 不得创建任何证据目录');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
if (prevKey !== undefined)
|
|
141
|
+
process.env['LLM_API_KEY'] = prevKey;
|
|
142
|
+
if (prevDeepseekKey !== undefined)
|
|
143
|
+
process.env['DEEPSEEK_API_KEY'] = prevDeepseekKey;
|
|
144
|
+
rmSync(waitParent, { recursive: true, force: true });
|
|
162
145
|
}
|
|
163
146
|
ok('无凭证 → waiting_external_evidence 且零写入');
|
|
147
|
+
// ---- 7. dry-run:mock 全链演练,记录为 real_llm=false 且绝不落盘 ----
|
|
164
148
|
const dryParent = mkdtempSync(join(tmpdir(), 'nightly-dry-'));
|
|
165
149
|
const dryRoot = join(dryParent, 'evidence', 'm3');
|
|
166
150
|
try {
|
|
167
|
-
const dry = await runNightlyEvidence({
|
|
168
|
-
evidenceRoot: dryRoot,
|
|
169
|
-
dryRun: true,
|
|
170
|
-
clock: ()=>new Date(executeAt)
|
|
171
|
-
});
|
|
151
|
+
const dry = await runNightlyEvidence({ evidenceRoot: dryRoot, dryRun: true, clock: () => new Date(executeAt) });
|
|
172
152
|
assert.equal(dry.state, 'dry_run');
|
|
173
153
|
assert.equal(dry.record?.real_llm, false);
|
|
174
154
|
parseNightlyRun(JSON.parse(JSON.stringify(dry.record)), 0);
|
|
175
155
|
assert.equal(existsSync(dryRoot), false, 'dry-run 不得写任何证据文件');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
force: true
|
|
180
|
-
});
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
rmSync(dryParent, { recursive: true, force: true });
|
|
181
159
|
}
|
|
182
160
|
ok('dry-run mock 全链演练 + real_llm=false + 零落盘');
|
|
183
161
|
console.log(`nightly-evidence tests: ${passed} 组断言全绿(offline,真跑不在 CI)`);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
//# sourceURL=ts/scripts/nightly-evidence-tests.ts
|