@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,55 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 记忆效用 sidecar(RFC S2,post-outcome-memory-utility-attribution 映射):
|
|
3
|
+
* 「被召回 ≠ 有用,自称用了 ≠ 让结果变好」——召回/使用/结果/归因四件语义独立记录,
|
|
4
|
+
* 确定性 reducer 只做投影;verified_outcome 只能来自 owner 确认(owner_correction tier),
|
|
5
|
+
* 模型永远不能自称「有用」。默认 fail-open:sidecar 缺失/损坏不阻塞主路径。
|
|
6
|
+
*
|
|
7
|
+
* 多用户 AaaS 前向兼容(RFC §6.5):append-only 事件流 + 稳定 wish_id,
|
|
8
|
+
* 未来账本化(CAS/receipt)是存储面替换,语义层零改造。
|
|
9
|
+
*/
|
|
10
|
+
/** 事件幂等键:同 wish 同类同 ctx 同归因 = 同一事件(重放 no-op) */
|
|
1
11
|
export function makeEventId(ev) {
|
|
2
|
-
return [
|
|
3
|
-
ev.wish_id,
|
|
4
|
-
ev.kind,
|
|
5
|
-
ev.ctx ?? '',
|
|
6
|
-
ev.attribution ?? ''
|
|
7
|
-
].join('|');
|
|
12
|
+
return [ev.wish_id, ev.kind, ev.ctx ?? '', ev.attribution ?? ''].join('|');
|
|
8
13
|
}
|
|
14
|
+
/** 追加不重复(纯函数,不改入参):同 event_id 已存在 → no-op */
|
|
9
15
|
export function appendEvent(events, ev) {
|
|
10
|
-
const full = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
...ev
|
|
14
|
-
};
|
|
15
|
-
if (events.some((e)=>e.event_id === full.event_id)) return {
|
|
16
|
-
events,
|
|
17
|
-
appended: false
|
|
18
|
-
};
|
|
16
|
+
const full = { schema: 'memory_utility_observation.v0', event_id: makeEventId(ev), ...ev };
|
|
17
|
+
if (events.some(e => e.event_id === full.event_id))
|
|
18
|
+
return { events, appended: false };
|
|
19
19
|
if (ev.kind === 'verified_outcome' && !ev.attribution) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
kind: 'applied'
|
|
23
|
-
});
|
|
20
|
+
// 无归因的结果不算归因证据:降级为 applied(六件语义不越界)
|
|
21
|
+
return appendEvent(events, { ...ev, kind: 'applied' });
|
|
24
22
|
}
|
|
25
|
-
return {
|
|
26
|
-
events: [
|
|
27
|
-
...events,
|
|
28
|
-
full
|
|
29
|
-
],
|
|
30
|
-
appended: true
|
|
31
|
-
};
|
|
23
|
+
return { events: [...events, full], appended: true };
|
|
32
24
|
}
|
|
25
|
+
/** 只读投影:status = 最后一次 verified_outcome 的归因;无 verified_outcome 永远 unknown */
|
|
33
26
|
export function projectUtility(events) {
|
|
34
27
|
const out = {};
|
|
35
|
-
for (const ev of events){
|
|
36
|
-
const w = out[ev.wish_id] ??= {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
applied
|
|
41
|
-
verified: 0
|
|
42
|
-
};
|
|
43
|
-
if (ev.kind === 'recalled') w.recalled++;
|
|
44
|
-
if (ev.kind === 'applied') w.applied++;
|
|
28
|
+
for (const ev of events) {
|
|
29
|
+
const w = out[ev.wish_id] ??= { wish_id: ev.wish_id, status: 'unknown', recalled: 0, applied: 0, verified: 0 };
|
|
30
|
+
if (ev.kind === 'recalled')
|
|
31
|
+
w.recalled++;
|
|
32
|
+
if (ev.kind === 'applied')
|
|
33
|
+
w.applied++;
|
|
45
34
|
if (ev.kind === 'verified_outcome') {
|
|
46
35
|
w.verified++;
|
|
47
|
-
if (ev.attribution)
|
|
36
|
+
if (ev.attribution)
|
|
37
|
+
w.status = ev.attribution;
|
|
48
38
|
}
|
|
49
39
|
w.last_event_ts = ev.ts;
|
|
50
40
|
}
|
|
51
41
|
return out;
|
|
52
42
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//# sourceURL=ts/src/memory-utility.ts
|
package/dist/src/mock-llm.js
CHANGED
|
@@ -1,103 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock LLM(S2,ADR-8):确定性剧本,重放真实对话的智能侧。
|
|
3
|
+
* 它不做任何「聪明事」——只按剧本返回抽取结果;循环架构的正确性由此验证,
|
|
4
|
+
* 与智能质量解耦。S4 接真 LLM 后,本文件留作回归夹具。
|
|
5
|
+
*/
|
|
1
6
|
import { parseFlightPackToSpec } from './unified.js';
|
|
7
|
+
import { mergeProfileWorkWindow } from './flight-pack-adapter.js';
|
|
8
|
+
/** 剧本:来自真实 Kimi 对话里用户给出关键事实的时刻 */
|
|
2
9
|
const SCRIPT = [
|
|
3
10
|
{
|
|
4
11
|
when: '请给我做机票和酒店的行程规划和推荐',
|
|
5
12
|
calendar: {
|
|
6
13
|
year: 2026,
|
|
7
14
|
assertedWeekdays: {
|
|
8
|
-
'2026-07-17': 'fri',
|
|
9
|
-
'2026-
|
|
10
|
-
|
|
11
|
-
'2026-08-01': 'sat',
|
|
12
|
-
'2026-08-09': 'sun',
|
|
13
|
-
'2026-08-10': 'mon'
|
|
14
|
-
}
|
|
15
|
+
'2026-07-17': 'fri', '2026-07-18': 'sat', '2026-07-31': 'fri',
|
|
16
|
+
'2026-08-01': 'sat', '2026-08-09': 'sun', '2026-08-10': 'mon',
|
|
17
|
+
},
|
|
15
18
|
},
|
|
16
|
-
profile: {
|
|
17
|
-
companions: [
|
|
18
|
-
'girlfriend(先在普吉)'
|
|
19
|
-
]
|
|
20
|
-
}
|
|
19
|
+
profile: { companions: ['girlfriend(先在普吉)'] },
|
|
21
20
|
},
|
|
22
21
|
{
|
|
23
22
|
when: '工作时间',
|
|
24
23
|
profile: {
|
|
25
24
|
workWindow: {
|
|
26
|
-
homeTzOffsetMin: 240,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
0,
|
|
31
|
-
1,
|
|
32
|
-
2,
|
|
33
|
-
3,
|
|
34
|
-
4
|
|
35
|
-
],
|
|
36
|
-
evidence: '用户原话:我的工作时间是UTC+4的早上10点到下午7点'
|
|
37
|
-
}
|
|
38
|
-
}
|
|
25
|
+
homeTzOffsetMin: 240, startMin: 600, endMin: 1140, workdays: [0, 1, 2, 3, 4],
|
|
26
|
+
evidence: '用户原话:我的工作时间是UTC+4的早上10点到下午7点',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
39
29
|
},
|
|
40
30
|
{
|
|
41
31
|
when: '订了酒店',
|
|
42
32
|
profile: {
|
|
43
33
|
bookedResources: [
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
34
|
+
{ kind: 'hotel', ref: 'The Title East Wing Rawai(7.18-23, 5 晚)', window: '2026-07-18~07-23' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
52
38
|
];
|
|
53
39
|
export function createMockLlm(flightPackPath, slotScript = []) {
|
|
54
40
|
let stepIdx = 0;
|
|
55
41
|
return {
|
|
56
|
-
async extractFacts
|
|
42
|
+
async extractFacts(history, _state) {
|
|
57
43
|
const last = history[history.length - 1]?.text ?? '';
|
|
44
|
+
// 剧本顺序消费:真实对话里事实按此顺序浮出;未命中则无新事实
|
|
58
45
|
const step = SCRIPT[stepIdx];
|
|
59
46
|
if (step && last.includes(step.when)) {
|
|
60
47
|
stepIdx++;
|
|
61
48
|
return {
|
|
62
49
|
calendar: step.calendar,
|
|
63
50
|
profile: step.profile,
|
|
64
|
-
assumptions: Object.keys({
|
|
65
|
-
...step.profile
|
|
66
|
-
}).map((f)=>({
|
|
67
|
-
field: f,
|
|
68
|
-
source: 'user-verbatim'
|
|
69
|
-
}))
|
|
51
|
+
assumptions: Object.keys({ ...step.profile }).map(f => ({ field: f, source: 'user-verbatim' })),
|
|
70
52
|
};
|
|
71
53
|
}
|
|
72
|
-
return {
|
|
73
|
-
assumptions: []
|
|
74
|
-
};
|
|
54
|
+
return { assumptions: [] };
|
|
75
55
|
},
|
|
76
|
-
async extractSpec
|
|
77
|
-
|
|
78
|
-
if (!
|
|
56
|
+
async extractSpec(history, state) {
|
|
57
|
+
// mock 翻译:工作窗口与已订资源齐备后,「翻译」= 装载航班包并挂上真实工作窗口
|
|
58
|
+
if (!state.profile.workWindow || !state.profile.bookedResources)
|
|
59
|
+
return null;
|
|
60
|
+
if (!flightPackPath)
|
|
61
|
+
return null;
|
|
79
62
|
const { readFile } = await import('node:fs/promises');
|
|
80
63
|
const pack = JSON.parse(await readFile(flightPackPath, 'utf-8'));
|
|
81
64
|
const spec = parseFlightPackToSpec(pack);
|
|
82
|
-
spec.workWindow
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
endMin: state.profile.workWindow.endMin,
|
|
86
|
-
workdays: state.profile.workWindow.workdays
|
|
87
|
-
};
|
|
88
|
-
spec.budgetCny = 9000;
|
|
89
|
-
return spec;
|
|
65
|
+
const merged = mergeProfileWorkWindow(spec, state.profile.workWindow);
|
|
66
|
+
merged.budgetCny = 9000;
|
|
67
|
+
return merged;
|
|
90
68
|
},
|
|
91
|
-
async extractSlots
|
|
69
|
+
async extractSlots(history) {
|
|
92
70
|
const last = history[history.length - 1]?.text ?? '';
|
|
93
|
-
const hit = slotScript.find(
|
|
71
|
+
const hit = slotScript.find(s => last.includes(s.when));
|
|
94
72
|
return hit ? structuredClone(hit.extraction) : null;
|
|
95
73
|
},
|
|
96
|
-
async polishQuestion
|
|
74
|
+
async polishQuestion(q) {
|
|
97
75
|
return `【${q.key}】${q.text}\n(为什么问:${q.why})`;
|
|
98
|
-
}
|
|
76
|
+
},
|
|
99
77
|
};
|
|
100
78
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//# sourceURL=ts/src/mock-llm.ts
|
package/dist/src/model.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gotry 可行性引擎领域模型(TS 版,与 py/gotry_feasibility/model.py 逐行对齐)。
|
|
3
|
+
*
|
|
4
|
+
* 双实现纪律:Python 版是**对照实现(oracle)**,TS 版是产品运行时实现;
|
|
5
|
+
* 同一输入两版必须给出相同判定(ts/scripts/diff-test.ts 做差分验证)。
|
|
6
|
+
* 算术(门到门全成本)在此层与求解完全分离,两边各自可测。
|
|
7
|
+
*/
|
|
1
8
|
export function hhmmToMin(s) {
|
|
2
9
|
const [h, m] = s.split(':');
|
|
3
10
|
return Number(h) * 60 + Number(m);
|
|
@@ -8,6 +15,7 @@ export function minToHhmm(m) {
|
|
|
8
15
|
export function requiredUsableHours(mot) {
|
|
9
16
|
return mot.baseUsableHours + mot.escapeHoursPerWeight * (mot.weights['escape_rest'] ?? 0);
|
|
10
17
|
}
|
|
18
|
+
// ---- 精力模型(可校准参数,与 Python 版一致) --------------------------------
|
|
11
19
|
export const WAKE_PENALTY_BEFORE_5 = 30;
|
|
12
20
|
export const WAKE_PENALTY_BEFORE_6 = 25;
|
|
13
21
|
export const WAKE_PENALTY_BEFORE_630 = 15;
|
|
@@ -19,56 +27,46 @@ export const DAY2_START_MIN = 9 * 60;
|
|
|
19
27
|
export const DAY2_QUALITY = 0.9;
|
|
20
28
|
export const LATEST_ARRIVE_STAY_MIN = 18 * 60;
|
|
21
29
|
export function parseMotivation(d) {
|
|
22
|
-
const hard = d['hard'] ?? {};
|
|
23
|
-
const rawWeights = d['weights'] ?? d;
|
|
30
|
+
const hard = (d['hard'] ?? {});
|
|
31
|
+
const rawWeights = (d['weights'] ?? d);
|
|
24
32
|
const weights = {};
|
|
25
|
-
for (const [k, v] of Object.entries(rawWeights)){
|
|
26
|
-
if (typeof v === 'number')
|
|
33
|
+
for (const [k, v] of Object.entries(rawWeights)) {
|
|
34
|
+
if (typeof v === 'number')
|
|
35
|
+
weights[k] = v;
|
|
27
36
|
}
|
|
28
37
|
return {
|
|
29
38
|
weights,
|
|
30
39
|
wakeFloorMin: hhmmToMin(String(hard['wake_not_before'] ?? '06:30')),
|
|
31
40
|
minArrivalEnergyPct: Number(hard['min_arrival_energy_pct'] ?? 40),
|
|
32
41
|
baseUsableHours: 4.0,
|
|
33
|
-
escapeHoursPerWeight: 2.0
|
|
42
|
+
escapeHoursPerWeight: 2.0,
|
|
34
43
|
};
|
|
35
44
|
}
|
|
36
45
|
export function parseRequest(d) {
|
|
37
46
|
const home = (d['home'] ?? {})['hubs'] ?? {};
|
|
38
47
|
const homeHubAccess = {};
|
|
39
|
-
for (const [hub, acc] of Object.entries(home)){
|
|
40
|
-
homeHubAccess[hub] = {
|
|
41
|
-
hub,
|
|
42
|
-
toHubMin: Number(acc['to_hub_min'])
|
|
43
|
-
};
|
|
48
|
+
for (const [hub, acc] of Object.entries(home)) {
|
|
49
|
+
homeHubAccess[hub] = { hub, toHubMin: Number(acc['to_hub_min']) };
|
|
44
50
|
}
|
|
45
51
|
return {
|
|
46
52
|
note: String(d['note'] ?? ''),
|
|
47
53
|
motivation: parseMotivation(d['motivation']),
|
|
48
54
|
windowDays: Number(d['window_days']),
|
|
49
55
|
budgetCny: Number(d['budget_cny']),
|
|
50
|
-
homeHubAccess
|
|
56
|
+
homeHubAccess,
|
|
51
57
|
};
|
|
52
58
|
}
|
|
53
59
|
function parseService(d) {
|
|
54
|
-
return {
|
|
55
|
-
id: String(d['id']),
|
|
56
|
-
depMin: hhmmToMin(String(d['dep'])),
|
|
57
|
-
arrMin: hhmmToMin(String(d['arr'])),
|
|
58
|
-
priceCny: Number(d['price_cny'])
|
|
59
|
-
};
|
|
60
|
+
return { id: String(d['id']), depMin: hhmmToMin(String(d['dep'])), arrMin: hhmmToMin(String(d['arr'])), priceCny: Number(d['price_cny']) };
|
|
60
61
|
}
|
|
61
62
|
function parseTransfer(d) {
|
|
62
|
-
return {
|
|
63
|
-
mode: String(d['mode']),
|
|
64
|
-
minutes: Number(d['min']),
|
|
65
|
-
priceCny: Number(d['price_cny'])
|
|
66
|
-
};
|
|
63
|
+
return { mode: String(d['mode']), minutes: Number(d['min']), priceCny: Number(d['price_cny']) };
|
|
67
64
|
}
|
|
68
65
|
export function parseCandidate(d) {
|
|
69
66
|
return {
|
|
70
67
|
id: String(d['id']),
|
|
71
68
|
name: String(d['name']),
|
|
69
|
+
date: typeof d['date'] === 'string' ? d['date'] : undefined,
|
|
72
70
|
hub: String(d['hub']),
|
|
73
71
|
bufferOutMin: Number(d['buffer_out_min'] ?? 60),
|
|
74
72
|
bufferRetMin: Number(d['buffer_ret_min'] ?? 60),
|
|
@@ -79,29 +77,79 @@ export function parseCandidate(d) {
|
|
|
79
77
|
localDailyCny: Number(d['local_daily_cny']),
|
|
80
78
|
minDaysForPurpose: Number(d['min_days_for_purpose']),
|
|
81
79
|
imageryMatch: Number(d['imagery_match']),
|
|
82
|
-
bestMonths: (d['best_months'] ?? []).map(Number)
|
|
80
|
+
bestMonths: (d['best_months'] ?? []).map(Number),
|
|
83
81
|
};
|
|
84
82
|
}
|
|
83
|
+
/** Issue #343 d2d canonical arithmetic。 */
|
|
84
|
+
export class NonpositiveDurationError extends Error {
|
|
85
|
+
/** 'arr_not_after_dep' | 'legacy_negative_offset' */
|
|
86
|
+
kind;
|
|
87
|
+
code = 'nonpositive_duration';
|
|
88
|
+
constructor(kind, message) {
|
|
89
|
+
super(message);
|
|
90
|
+
this.kind = kind;
|
|
91
|
+
this.name = 'NonpositiveDurationError';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 单段门到门时长(分)。v2 路径用真实 UTC instant(跨日 / 反向日界线 / DST 切换正确),
|
|
96
|
+
* 且强制 `arrUtcMs > depUtcMs` — parse 边界也会再做一次防御性 throw。
|
|
97
|
+
* v1 路径用墙上分钟差 - tzOffsetMin(原数值口径,字节不变,即使结果为负也照旧返回)。
|
|
98
|
+
*
|
|
99
|
+
* v2:arrUtcMs 必须严格大于 depUtcMs;否则抛 NonpositiveDurationError(arr_not_after_dep)。
|
|
100
|
+
* v1:沿用 legacy 公式,字节不变;不抛错。
|
|
101
|
+
*
|
|
102
|
+
* 调用方(unified.ts evaluateOptionMove)只负责拿到 doorToDoorMin 并包装 LegReport。
|
|
103
|
+
*/
|
|
104
|
+
export function doorToDoorFromMove(svc, mv) {
|
|
105
|
+
if (typeof svc.depUtcMs === 'number' && typeof svc.arrUtcMs === 'number') {
|
|
106
|
+
if (svc.arrUtcMs <= svc.depUtcMs) {
|
|
107
|
+
throw new NonpositiveDurationError('arr_not_after_dep', `service ${svc.id}: arrUtcMs (${svc.arrUtcMs}) must be strictly greater than depUtcMs (${svc.depUtcMs})`);
|
|
108
|
+
}
|
|
109
|
+
const trueFlightMin = (svc.arrUtcMs - svc.depUtcMs) / 60_000;
|
|
110
|
+
const doorToDoorMin = mv.bufferMin + mv.originTransferMin + trueFlightMin + mv.destTransferMin;
|
|
111
|
+
return { trueFlightMin, doorToDoorMin };
|
|
112
|
+
}
|
|
113
|
+
// v1 legacy 字节不变:不校验正负,沿用原公式。
|
|
114
|
+
const trueFlightMin = (svc.arrMin - svc.depMin) - (mv.tzOffsetMin ?? 0);
|
|
115
|
+
const doorToDoorMin = mv.bufferMin + mv.originTransferMin + trueFlightMin + mv.destTransferMin;
|
|
116
|
+
return { trueFlightMin, doorToDoorMin };
|
|
117
|
+
}
|
|
85
118
|
export function evaluateChoice(cand, req, ch) {
|
|
119
|
+
/** 门到门全成本核算(与 Python evaluate_choice 完全一致)。 */
|
|
86
120
|
const access = req.homeHubAccess[cand.hub];
|
|
121
|
+
const outTransferMin = typeof ch.outTransfer.minutesOut === 'number'
|
|
122
|
+
? ch.outTransfer.minutesOut
|
|
123
|
+
: ch.outTransfer.minutes;
|
|
124
|
+
const retTransferMin = typeof ch.retTransfer.minutesRet === 'number'
|
|
125
|
+
? ch.retTransfer.minutesRet
|
|
126
|
+
: ch.retTransfer.minutes;
|
|
87
127
|
const wake = ch.outService.depMin - cand.bufferOutMin - access.toHubMin;
|
|
88
|
-
const arriveStay = ch.outService.arrMin +
|
|
128
|
+
const arriveStay = ch.outService.arrMin + outTransferMin;
|
|
89
129
|
const d2dOut = arriveStay - wake;
|
|
90
130
|
let energy = 100;
|
|
91
|
-
if (wake < 5 * 60)
|
|
92
|
-
|
|
93
|
-
else if (wake <
|
|
131
|
+
if (wake < 5 * 60)
|
|
132
|
+
energy -= WAKE_PENALTY_BEFORE_5;
|
|
133
|
+
else if (wake < 6 * 60)
|
|
134
|
+
energy -= WAKE_PENALTY_BEFORE_6;
|
|
135
|
+
else if (wake < hhmmToMin('06:30'))
|
|
136
|
+
energy -= WAKE_PENALTY_BEFORE_630;
|
|
94
137
|
energy -= 2 * TRANSFER_PENALTY;
|
|
95
|
-
if (arriveStay > 21 * 60)
|
|
96
|
-
|
|
138
|
+
if (arriveStay > 21 * 60)
|
|
139
|
+
energy -= LATE_ARRIVAL_PENALTY;
|
|
140
|
+
if (d2dOut > 6 * 60)
|
|
141
|
+
energy -= LONG_D2D_PENALTY;
|
|
97
142
|
energy = Math.max(0, energy);
|
|
98
143
|
const day1Raw = Math.max(0, DAY_END_MIN - arriveStay) / 60;
|
|
99
144
|
const day1 = day1Raw * (0.5 + energy / 200);
|
|
100
|
-
const leaveStayRet = ch.retService.depMin - cand.bufferRetMin -
|
|
145
|
+
const leaveStayRet = ch.retService.depMin - cand.bufferRetMin - retTransferMin;
|
|
101
146
|
const day2 = Math.max(0, leaveStayRet - DAY2_START_MIN) / 60 * DAY2_QUALITY;
|
|
102
147
|
const midDays = Math.max(0, ch.days - 2);
|
|
103
148
|
const usable = day1 + day2 + midDays * 8.0 * DAY2_QUALITY;
|
|
104
|
-
const money = ch.outService.priceCny + ch.retService.priceCny
|
|
149
|
+
const money = ch.outService.priceCny + ch.retService.priceCny
|
|
150
|
+
+ ch.outTransfer.priceCny + ch.retTransfer.priceCny
|
|
151
|
+
+ cand.stayCnyPerNight * (ch.days - 1)
|
|
152
|
+
+ cand.localDailyCny * ch.days;
|
|
105
153
|
return {
|
|
106
154
|
moneyCny: money,
|
|
107
155
|
wakeMin: wake,
|
|
@@ -112,7 +160,7 @@ export function evaluateChoice(cand, req, ch) {
|
|
|
112
160
|
usableDay1Hours: day1,
|
|
113
161
|
usableDay2Hours: day2,
|
|
114
162
|
departHomeRetMin: leaveStayRet,
|
|
115
|
-
arriveHomeRetMin: ch.retService.arrMin + access.toHubMin
|
|
163
|
+
arriveHomeRetMin: ch.retService.arrMin + access.toHubMin,
|
|
116
164
|
};
|
|
117
165
|
}
|
|
118
166
|
export function trueCostToDict(t) {
|
|
@@ -126,9 +174,6 @@ export function trueCostToDict(t) {
|
|
|
126
174
|
usable_day1_hours: Math.round(t.usableDay1Hours * 10) / 10,
|
|
127
175
|
usable_day2_hours: Math.round(t.usableDay2Hours * 10) / 10,
|
|
128
176
|
leave_stay_return: minToHhmm(t.departHomeRetMin),
|
|
129
|
-
arrive_home_return: minToHhmm(Math.min(1440, t.arriveHomeRetMin))
|
|
177
|
+
arrive_home_return: minToHhmm(Math.min(1440, t.arriveHomeRetMin)),
|
|
130
178
|
};
|
|
131
179
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
//# sourceURL=ts/src/model.ts
|
|
@@ -1,51 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 实时票价 overlay(README Known limitation「机票实时数据」清偿件):
|
|
3
|
+
* 静态包(data/flights_2026.json)仍是兜底,不再是唯一票价源——dated 航班链 spec
|
|
4
|
+
* (段带 date + route 词表内城市对)可经 FlyAI 官方只读通道(gotry_flyai_search 同源
|
|
5
|
+
* `capabilities/flyai.ts`)取实时价,按航班号精确匹配覆写 spec 价格后进求解,
|
|
6
|
+
* 证据链 `[实时API:flyai@ts]` 随行(data-sources §6)。
|
|
7
|
+
*
|
|
8
|
+
* 三值语义(永不抛错,与 hbcli/weather/opensky/anything 同构,L4 不变量):
|
|
9
|
+
* - hit 且价正 → 覆写 + 记 evidence(静态原价留档对账);
|
|
10
|
+
* - miss/error/打码价(如 "1xxx",非正数)/无匹配 → 段原样保留(静态价即降级);
|
|
11
|
+
* - 查询实现注入(测试离线确定性);默认关,`GOTRY_REALTIME_PRICING=1` 开启产品面。
|
|
12
|
+
*/
|
|
1
13
|
import { interpretEffect } from '../capabilities/effect.js';
|
|
14
|
+
/** FlyAI CLI 收中文城市名;route 提示是 IATA 码(spec 层骨架词表,与 skeleton-check 同源) */
|
|
2
15
|
const IATA_TO_CITY = {
|
|
3
|
-
HKG: '香港',
|
|
4
|
-
|
|
5
|
-
BKK: '曼谷',
|
|
6
|
-
KMG: '昆明',
|
|
7
|
-
SZX: '深圳',
|
|
8
|
-
DXB: '迪拜',
|
|
9
|
-
LJG: '丽江',
|
|
10
|
-
DLU: '大理',
|
|
11
|
-
SHA: '上海',
|
|
12
|
-
PVG: '上海',
|
|
13
|
-
PEK: '北京',
|
|
14
|
-
CTU: '成都'
|
|
16
|
+
HKG: '香港', HKT: '普吉', BKK: '曼谷', KMG: '昆明', SZX: '深圳', DXB: '迪拜',
|
|
17
|
+
LJG: '丽江', DLU: '大理', SHA: '上海', PVG: '上海', PEK: '北京', CTU: '成都',
|
|
15
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* dated 航班链 spec 的实时票价覆写:
|
|
21
|
+
* - 只碰 `date`(YYYY-MM-DD)且 route 落在词表内的段;火车段(无 route 词表)原样;
|
|
22
|
+
* - 段内 option 按 service id(=航班号,大小写不敏感)与 FlyAI 条目精确匹配;
|
|
23
|
+
* - 任何失败(查询 error/miss、价格非正数、无匹配)→ 该段原样保留,绝不抛错。
|
|
24
|
+
*/
|
|
16
25
|
export async function overlayRealtimeFlightPrices(spec, opts = {}) {
|
|
17
|
-
const out = {
|
|
18
|
-
spec,
|
|
19
|
-
matched: [],
|
|
20
|
-
notes: []
|
|
21
|
-
};
|
|
26
|
+
const out = { spec, matched: [], notes: [] };
|
|
22
27
|
const flights = [];
|
|
23
|
-
for (const seg of spec.segments){
|
|
24
|
-
if (!seg.date || !/^\d{4}-\d{2}-\d{2}$/.test(seg.date))
|
|
28
|
+
for (const seg of spec.segments) {
|
|
29
|
+
if (!seg.date || !/^\d{4}-\d{2}-\d{2}$/.test(seg.date))
|
|
30
|
+
continue;
|
|
25
31
|
const route = seg.route;
|
|
26
|
-
if (!route || !route.includes('->'))
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
destination: b
|
|
35
|
-
});
|
|
32
|
+
if (!route || !route.includes('->'))
|
|
33
|
+
continue;
|
|
34
|
+
const [a, b] = route.split('->').map(s => IATA_TO_CITY[s.trim()]);
|
|
35
|
+
if (!a || !b)
|
|
36
|
+
continue;
|
|
37
|
+
if (seg.options.some(o => !o.move))
|
|
38
|
+
continue;
|
|
39
|
+
flights.push({ seg, date: seg.date, origin: a, destination: b });
|
|
36
40
|
}
|
|
37
|
-
if (flights.length === 0)
|
|
41
|
+
if (flights.length === 0)
|
|
42
|
+
return out;
|
|
38
43
|
const query = opts.query ?? defaultQuery;
|
|
39
|
-
for (const f of flights){
|
|
44
|
+
for (const f of flights) {
|
|
40
45
|
let result;
|
|
41
46
|
try {
|
|
42
|
-
result = await query({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
destination: f.destination,
|
|
46
|
-
depDate: f.date
|
|
47
|
-
});
|
|
48
|
-
} catch (e) {
|
|
47
|
+
result = await query({ kind: 'flight', origin: f.origin, destination: f.destination, depDate: f.date });
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
49
50
|
out.notes.push(`${f.seg.id}: 实时票价不可用(降级静态包):${e.message?.slice(0, 80)} [实时API:flyai@error]`);
|
|
50
51
|
continue;
|
|
51
52
|
}
|
|
@@ -55,58 +56,48 @@ export async function overlayRealtimeFlightPrices(spec, opts = {}) {
|
|
|
55
56
|
}
|
|
56
57
|
const ts = /@(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]/.exec(result.evidence)?.[1] ?? new Date().toISOString();
|
|
57
58
|
const byNo = new Map();
|
|
58
|
-
for (const o of result.options){
|
|
59
|
-
if (Number.isFinite(o.price) && o.price > 0)
|
|
59
|
+
for (const o of result.options) {
|
|
60
|
+
if (Number.isFinite(o.price) && o.price > 0)
|
|
61
|
+
byNo.set(o.no.toUpperCase(), o);
|
|
60
62
|
}
|
|
61
|
-
for (const opt of f.seg.options){
|
|
63
|
+
for (const opt of f.seg.options) {
|
|
62
64
|
const svc = opt.move.services[0];
|
|
63
65
|
const rt = byNo.get(svc.id.toUpperCase());
|
|
64
|
-
if (!rt)
|
|
66
|
+
if (!rt)
|
|
67
|
+
continue;
|
|
65
68
|
const evidence = `[实时API:flyai@${ts}] ${rt.no} ¥${rt.price}(静态包原价 ¥${svc.priceCny})`;
|
|
66
|
-
out.matched.push({
|
|
67
|
-
segment: f.seg.id,
|
|
68
|
-
option: opt.id,
|
|
69
|
-
staticCny: svc.priceCny,
|
|
70
|
-
realtimeCny: rt.price,
|
|
71
|
-
evidence
|
|
72
|
-
});
|
|
69
|
+
out.matched.push({ segment: f.seg.id, option: opt.id, staticCny: svc.priceCny, realtimeCny: rt.price, evidence });
|
|
73
70
|
out.notes.push(evidence);
|
|
74
71
|
svc.priceCny = rt.price;
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
return out;
|
|
78
75
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
destination: q.destination,
|
|
86
|
-
depDate: q.depDate
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
if (!itp.result) throw new Error(itp.trace.evidence.join(';'));
|
|
76
|
+
/** 默认查询面走效应解译器(ADR-18):多段行程连查享有退避重试/断路器保护——
|
|
77
|
+
* 断路拒绝(上游连环失败)时抛错,一并落在「实时票价不可用(降级静态包)」既有兜底里 */
|
|
78
|
+
const defaultQuery = async (q) => {
|
|
79
|
+
const itp = await interpretEffect({ effect: 'FLYAI_SEARCH', params: { kind: q.kind, origin: q.origin, destination: q.destination, depDate: q.depDate } });
|
|
80
|
+
if (!itp.result)
|
|
81
|
+
throw new Error(itp.trace.evidence.join(';'));
|
|
90
82
|
return itp.result;
|
|
91
83
|
};
|
|
84
|
+
/** 求解端口包装(产品接缝):`GOTRY_REALTIME_PRICING=1`(或 opts.enabled)时 dated 航班链段
|
|
85
|
+
* 先经 FlyAI 实时价覆写,证据行并进结果 skeleton_notes;关闸/零覆写零改动,永不抛错。 */
|
|
92
86
|
export function realtimeSolvePort(base, opts = {}) {
|
|
93
|
-
return async (spec)=>{
|
|
87
|
+
return async (spec) => {
|
|
94
88
|
const enabled = opts.enabled ?? process.env['GOTRY_REALTIME_PRICING'] === '1';
|
|
95
|
-
if (!enabled)
|
|
89
|
+
if (!enabled)
|
|
90
|
+
return base(spec);
|
|
96
91
|
try {
|
|
97
92
|
const ov = await overlayRealtimeFlightPrices(spec, opts);
|
|
98
|
-
if (ov.matched.length === 0 && ov.notes.length === 0)
|
|
93
|
+
if (ov.matched.length === 0 && ov.notes.length === 0)
|
|
94
|
+
return base(ov.spec);
|
|
99
95
|
const res = await base(ov.spec);
|
|
100
|
-
res.skeleton_notes = [
|
|
101
|
-
...res.skeleton_notes ?? [],
|
|
102
|
-
...ov.notes
|
|
103
|
-
];
|
|
96
|
+
res.skeleton_notes = [...(res.skeleton_notes ?? []), ...ov.notes];
|
|
104
97
|
return res;
|
|
105
|
-
}
|
|
106
|
-
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return base(spec); // 覆写层任何异常 → 纯静态包原路求解
|
|
107
101
|
}
|
|
108
102
|
};
|
|
109
103
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
//# sourceURL=ts/src/realtime-pricing.ts
|