@danceiny/gotry 0.0.1-rc.10 → 0.0.1-rc.12
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 +35 -24
- package/cordis.gotry-patch.yml +14 -7
- package/dist/capabilities/flyai.js +135 -30
- package/dist/capabilities/hbcli.js +14 -2
- package/dist/capabilities/session/benchmark.js +252 -0
- package/dist/capabilities/session/transport.js +14 -9
- package/dist/capabilities/session-consent.js +82 -0
- package/dist/capabilities/session-login.js +103 -0
- package/dist/capabilities/session-search.js +5 -3
- package/dist/capabilities/weather.js +72 -17
- package/dist/scripts/agent-reach-wrapper-tests.js +4 -0
- package/dist/scripts/async-collect.js +33 -5
- package/dist/scripts/hbcli-tests.js +25 -4
- package/dist/scripts/i18n-tests.js +34 -0
- package/dist/scripts/ledger-tests.js +134 -4
- package/dist/scripts/memory-value-report.js +379 -0
- package/dist/scripts/product-metrics.js +569 -0
- package/dist/scripts/realtime-pricing-tests.js +115 -0
- package/dist/scripts/replay-real.js +3 -1
- package/dist/scripts/session-benchmark.js +261 -0
- package/dist/scripts/session-login.js +28 -0
- package/dist/scripts/session-tests.js +263 -22
- package/dist/scripts/smoke.js +112 -14
- package/dist/scripts/weather-tests.js +8 -1
- package/dist/scripts/z3-race-tests.js +35 -0
- package/dist/src/engine.js +185 -116
- package/dist/src/i18n.js +77 -0
- package/dist/src/index.js +124 -5
- package/dist/src/journey.js +78 -82
- package/dist/src/loop.js +65 -14
- package/dist/src/realtime-pricing.js +105 -0
- package/dist/src/state-ledger.js +30 -4
- package/dist/src/unified.js +158 -105
- package/dist/src/z3-shared.js +18 -0
- package/package.json +5 -3
- package/ts/capabilities/flyai.ts +177 -22
- package/ts/capabilities/hbcli.ts +16 -4
- package/ts/capabilities/session/benchmark.ts +273 -0
- package/ts/capabilities/session/transport.ts +19 -8
- package/ts/capabilities/session-consent.ts +127 -0
- package/ts/capabilities/session-login.ts +130 -0
- package/ts/capabilities/session-search.ts +11 -4
- package/ts/capabilities/weather.ts +84 -19
- package/ts/scripts/async-collect.ts +48 -7
- package/ts/src/index.ts +107 -11
- package/ts/src/loop.ts +85 -12
- package/ts/src/state-ledger.ts +48 -4
- package/ts/src/unified.ts +40 -36
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { flyaiSearch } from '../capabilities/flyai.js';
|
|
2
|
+
const IATA_TO_CITY = {
|
|
3
|
+
HKG: '香港',
|
|
4
|
+
HKT: '普吉',
|
|
5
|
+
BKK: '曼谷',
|
|
6
|
+
KMG: '昆明',
|
|
7
|
+
SZX: '深圳',
|
|
8
|
+
DXB: '迪拜',
|
|
9
|
+
LJG: '丽江',
|
|
10
|
+
DLU: '大理',
|
|
11
|
+
SHA: '上海',
|
|
12
|
+
PVG: '上海',
|
|
13
|
+
PEK: '北京',
|
|
14
|
+
CTU: '成都'
|
|
15
|
+
};
|
|
16
|
+
export async function overlayRealtimeFlightPrices(spec, opts = {}) {
|
|
17
|
+
const out = {
|
|
18
|
+
spec,
|
|
19
|
+
matched: [],
|
|
20
|
+
notes: []
|
|
21
|
+
};
|
|
22
|
+
const flights = [];
|
|
23
|
+
for (const seg of spec.segments){
|
|
24
|
+
if (!seg.date || !/^\d{4}-\d{2}-\d{2}$/.test(seg.date)) continue;
|
|
25
|
+
const route = seg.route;
|
|
26
|
+
if (!route || !route.includes('->')) continue;
|
|
27
|
+
const [a, b] = route.split('->').map((s)=>IATA_TO_CITY[s.trim()]);
|
|
28
|
+
if (!a || !b) continue;
|
|
29
|
+
if (seg.options.some((o)=>!o.move)) continue;
|
|
30
|
+
flights.push({
|
|
31
|
+
seg,
|
|
32
|
+
date: seg.date,
|
|
33
|
+
origin: a,
|
|
34
|
+
destination: b
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (flights.length === 0) return out;
|
|
38
|
+
const query = opts.query ?? defaultQuery;
|
|
39
|
+
for (const f of flights){
|
|
40
|
+
let result;
|
|
41
|
+
try {
|
|
42
|
+
result = await query({
|
|
43
|
+
kind: 'flight',
|
|
44
|
+
origin: f.origin,
|
|
45
|
+
destination: f.destination,
|
|
46
|
+
depDate: f.date
|
|
47
|
+
});
|
|
48
|
+
} catch (e) {
|
|
49
|
+
out.notes.push(`${f.seg.id}: 实时票价不可用(降级静态包):${e.message?.slice(0, 80)} [实时API:flyai@error]`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!result.ok || result.verdict !== 'hit' || !result.options?.length) {
|
|
53
|
+
out.notes.push(`${f.seg.id}: 实时票价未取回(${result.verdict},降级静态包) ${result.evidence}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const ts = /@(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]/.exec(result.evidence)?.[1] ?? new Date().toISOString();
|
|
57
|
+
const byNo = new Map();
|
|
58
|
+
for (const o of result.options){
|
|
59
|
+
if (Number.isFinite(o.price) && o.price > 0) byNo.set(o.no.toUpperCase(), o);
|
|
60
|
+
}
|
|
61
|
+
for (const opt of f.seg.options){
|
|
62
|
+
const svc = opt.move.services[0];
|
|
63
|
+
const rt = byNo.get(svc.id.toUpperCase());
|
|
64
|
+
if (!rt) continue;
|
|
65
|
+
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
|
+
});
|
|
73
|
+
out.notes.push(evidence);
|
|
74
|
+
svc.priceCny = rt.price;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
const defaultQuery = (q)=>flyaiSearch({
|
|
80
|
+
kind: q.kind,
|
|
81
|
+
origin: q.origin,
|
|
82
|
+
destination: q.destination,
|
|
83
|
+
depDate: q.depDate
|
|
84
|
+
});
|
|
85
|
+
export function realtimeSolvePort(base, opts = {}) {
|
|
86
|
+
return async (spec)=>{
|
|
87
|
+
const enabled = opts.enabled ?? process.env['GOTRY_REALTIME_PRICING'] === '1';
|
|
88
|
+
if (!enabled) return base(spec);
|
|
89
|
+
try {
|
|
90
|
+
const ov = await overlayRealtimeFlightPrices(spec, opts);
|
|
91
|
+
if (ov.matched.length === 0 && ov.notes.length === 0) return base(ov.spec);
|
|
92
|
+
const res = await base(ov.spec);
|
|
93
|
+
res.skeleton_notes = [
|
|
94
|
+
...res.skeleton_notes ?? [],
|
|
95
|
+
...ov.notes
|
|
96
|
+
];
|
|
97
|
+
return res;
|
|
98
|
+
} catch {
|
|
99
|
+
return base(spec);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
//# sourceURL=/Users/bytedance/work/gotry/ts/src/realtime-pricing.ts
|
package/dist/src/state-ledger.js
CHANGED
|
@@ -543,21 +543,47 @@ export class StateLedger {
|
|
|
543
543
|
markStepDone(runId, name, result) {
|
|
544
544
|
this.db.prepare(`UPDATE workflow_steps SET status = 'done', result = ?, done_ts = ? WHERE run_id = ? AND name = ? AND tenant_id = ?`).run(JSON.stringify(result), new Date().toISOString(), runId, name, this.tenant);
|
|
545
545
|
}
|
|
546
|
-
|
|
546
|
+
finishWorkflowRun(id, status, eventKind, deliverable, terminalOutcome, actor = 'system:async-collect') {
|
|
547
547
|
const run = this.db.transaction(()=>{
|
|
548
|
-
this.db.prepare(`UPDATE workflow_runs SET status =
|
|
548
|
+
const update = this.db.prepare(`UPDATE workflow_runs SET status = ?, deliverable = ?, updated = ?
|
|
549
|
+
WHERE id = ? AND tenant_id = ? AND status = 'pending'`).run(status, deliverable, new Date().toISOString(), id, this.tenant);
|
|
550
|
+
if (update.changes === 0) {
|
|
551
|
+
const existing = this.getWorkflowRun(id);
|
|
552
|
+
if (existing?.status === status) return;
|
|
553
|
+
throw new Error(`workflow ${id} cannot transition ${existing?.status ?? 'missing'} -> ${status}`);
|
|
554
|
+
}
|
|
549
555
|
this.insertEvent({
|
|
550
556
|
actor,
|
|
551
|
-
kind:
|
|
557
|
+
kind: eventKind,
|
|
552
558
|
subjectId: id,
|
|
553
559
|
payload: {
|
|
554
|
-
bytes: deliverable.length
|
|
560
|
+
bytes: deliverable.length,
|
|
561
|
+
terminal_outcome: terminalOutcome
|
|
555
562
|
},
|
|
563
|
+
idemKey: `async-terminal:${id}`,
|
|
556
564
|
runId: id
|
|
557
565
|
});
|
|
558
566
|
});
|
|
559
567
|
run();
|
|
560
568
|
}
|
|
569
|
+
settleWorkflowRun(id, deliverable, terminalOutcome, actor = 'system:async-collect') {
|
|
570
|
+
this.finishWorkflowRun(id, 'settled', 'async.settled', deliverable, terminalOutcome, actor);
|
|
571
|
+
}
|
|
572
|
+
failWorkflowRun(id, deliverable, terminalOutcome, actor = 'system:async-collect') {
|
|
573
|
+
this.finishWorkflowRun(id, 'failed', 'async.failed', deliverable, terminalOutcome, actor);
|
|
574
|
+
}
|
|
575
|
+
getWorkflowTerminalOutcome(id) {
|
|
576
|
+
const row = this.db.prepare(`SELECT payload FROM events
|
|
577
|
+
WHERE tenant_id = ? AND run_id = ? AND kind IN ('async.settled', 'async.failed')
|
|
578
|
+
ORDER BY seq DESC LIMIT 1`).get(this.tenant, id);
|
|
579
|
+
if (!row) return null;
|
|
580
|
+
try {
|
|
581
|
+
const payload = JSON.parse(row.payload);
|
|
582
|
+
return payload.terminal_outcome ?? null;
|
|
583
|
+
} catch {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
561
587
|
requestPendingWrite(input, actor = 'system:writegate') {
|
|
562
588
|
const ts = new Date().toISOString();
|
|
563
589
|
const run = this.db.transaction(()=>{
|
package/dist/src/unified.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { checkConnectivity } from '../scripts/skeleton-check.js';
|
|
2
2
|
import { evaluateChoice, minToHhmm, hhmmToMin, requiredUsableHours, trueCostToDict, LATEST_ARRIVE_STAY_MIN } from './model.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!z3Promise) {
|
|
6
|
-
const { init } = await import('z3-solver');
|
|
7
|
-
z3Promise = (async ()=>(await init()).Context('main'))();
|
|
8
|
-
}
|
|
9
|
-
return z3Promise;
|
|
10
|
-
}
|
|
3
|
+
import { withZ3 } from './z3-shared.js';
|
|
4
|
+
import { t as i18nT } from './i18n.js';
|
|
11
5
|
const SEGMENT_ROUTES = {
|
|
12
6
|
f1: 'HKG->HKT',
|
|
13
7
|
f2: 'HKT->BKK',
|
|
@@ -165,7 +159,12 @@ function workWindowBlocks(spec, option) {
|
|
|
165
159
|
const start = ww.startMin + shift, end = ww.endMin + shift;
|
|
166
160
|
if (start <= dep && dep <= end) {
|
|
167
161
|
const hhmm = (m)=>`${String(Math.floor(m / 60)).padStart(2, '0')}:${String(m % 60).padStart(2, '0')}`;
|
|
168
|
-
return
|
|
162
|
+
return i18nT('un.workwindow_reason', {
|
|
163
|
+
wd: WEEKDAY_CN[option.depWeekday] ?? '',
|
|
164
|
+
dep: hhmm(dep),
|
|
165
|
+
start: hhmm(start),
|
|
166
|
+
end: hhmm(end)
|
|
167
|
+
});
|
|
169
168
|
}
|
|
170
169
|
return null;
|
|
171
170
|
}
|
|
@@ -250,91 +249,95 @@ async function solveUnifiedInner(spec) {
|
|
|
250
249
|
};
|
|
251
250
|
}
|
|
252
251
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
for (const seg of spec.segments){
|
|
260
|
-
const sels = seg.options.map((_, i)=>Bool.const(`${seg.id}_o${i}`));
|
|
261
|
-
allSels[seg.id] = sels;
|
|
262
|
-
const svcs = seg.options.map((o)=>o.move.services[0]);
|
|
263
|
-
svcOf[seg.id] = svcs;
|
|
264
|
-
const pick = (attr)=>Sum(...sels.map((s, i)=>If(s, Int.val(svcs[i][attr]), Int.val(0))));
|
|
265
|
-
const dep = pick('depMin'), arr = pick('arrMin');
|
|
266
|
-
const o0 = seg.options[0].move;
|
|
267
|
-
const wake = dep.sub(Int.val(o0.bufferMin + o0.originTransferMin));
|
|
268
|
-
const arriveStay = arr.add(Int.val(o0.destTransferMin));
|
|
269
|
-
if (seg.anchors?.arriveByMin !== undefined) assertions[`${seg.id}:arrive_by`] = arriveStay.le(Int.val(seg.anchors.arriveByMin));
|
|
270
|
-
if (seg.anchors?.departAfterMin !== undefined) assertions[`${seg.id}:depart_after`] = dep.ge(Int.val(seg.anchors.departAfterMin));
|
|
271
|
-
if (!seg.options.some((o)=>o.move?.redEye)) assertions[`${seg.id}:wake_floor`] = wake.ge(Int.val(wakeFloor));
|
|
272
|
-
}
|
|
273
|
-
const total = Sum(...spec.segments.flatMap((seg)=>allSels[seg.id].map((s, i)=>If(s, Int.val(svcOf[seg.id][i].priceCny), Int.val(0)))));
|
|
274
|
-
if (spec.budgetCny !== undefined) assertions['total:budget'] = total.le(Int.val(spec.budgetCny));
|
|
275
|
-
const exactlyOne = (sels)=>Sum(...sels.map((s)=>If(s, Int.val(1), Int.val(0)))).eq(Int.val(1));
|
|
276
|
-
const coreOf = (s)=>{
|
|
277
|
-
const v = s.unsatCore();
|
|
278
|
-
const out = [];
|
|
279
|
-
for(let i = 0; i < v.length(); i++)out.push(String(v.get(i)).replace(/^\|/, '').replace(/\|$/, ''));
|
|
280
|
-
return out;
|
|
281
|
-
};
|
|
282
|
-
const s = new Solver();
|
|
283
|
-
for (const sel of Object.values(allSels))s.add(exactlyOne(sel));
|
|
284
|
-
for (const [name, expr] of Object.entries(assertions))s.addAndTrack(expr, name);
|
|
285
|
-
const report = async (model)=>{
|
|
286
|
-
const out = [];
|
|
252
|
+
return withZ3('unified.solveUnifiedInner', async (z3)=>{
|
|
253
|
+
const { Bool, If, Int, Solver, Sum } = z3;
|
|
254
|
+
const wakeFloor = spec.defaultWakeFloorMin ?? hhmmToMin('06:00');
|
|
255
|
+
const allSels = {};
|
|
256
|
+
const assertions = {};
|
|
257
|
+
const svcOf = {};
|
|
287
258
|
for (const seg of spec.segments){
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
259
|
+
const sels = seg.options.map((_, i)=>Bool.const(`${seg.id}_o${i}`));
|
|
260
|
+
allSels[seg.id] = sels;
|
|
261
|
+
const svcs = seg.options.map((o)=>o.move.services[0]);
|
|
262
|
+
svcOf[seg.id] = svcs;
|
|
263
|
+
const pick = (attr)=>Sum(...sels.map((s, i)=>If(s, Int.val(svcs[i][attr]), Int.val(0))));
|
|
264
|
+
const dep = pick('depMin'), arr = pick('arrMin');
|
|
265
|
+
const o0 = seg.options[0].move;
|
|
266
|
+
const wake = dep.sub(Int.val(o0.bufferMin + o0.originTransferMin));
|
|
267
|
+
const arriveStay = arr.add(Int.val(o0.destTransferMin));
|
|
268
|
+
if (seg.anchors?.arriveByMin !== undefined) assertions[`${seg.id}:arrive_by`] = arriveStay.le(Int.val(seg.anchors.arriveByMin));
|
|
269
|
+
if (seg.anchors?.departAfterMin !== undefined) assertions[`${seg.id}:depart_after`] = dep.ge(Int.val(seg.anchors.departAfterMin));
|
|
270
|
+
if (!seg.options.some((o)=>o.move?.redEye)) assertions[`${seg.id}:wake_floor`] = wake.ge(Int.val(wakeFloor));
|
|
271
|
+
}
|
|
272
|
+
const total = Sum(...spec.segments.flatMap((seg)=>allSels[seg.id].map((s, i)=>If(s, Int.val(svcOf[seg.id][i].priceCny), Int.val(0)))));
|
|
273
|
+
if (spec.budgetCny !== undefined) assertions['total:budget'] = total.le(Int.val(spec.budgetCny));
|
|
274
|
+
const exactlyOne = (sels)=>Sum(...sels.map((s)=>If(s, Int.val(1), Int.val(0)))).eq(Int.val(1));
|
|
275
|
+
const coreOf = (s)=>{
|
|
276
|
+
const v = s.unsatCore();
|
|
277
|
+
const out = [];
|
|
278
|
+
for(let i = 0; i < v.length(); i++)out.push(String(v.get(i)).replace(/^\|/, '').replace(/\|$/, ''));
|
|
279
|
+
return out;
|
|
280
|
+
};
|
|
281
|
+
const s = new Solver();
|
|
282
|
+
for (const sel of Object.values(allSels))s.add(exactlyOne(sel));
|
|
283
|
+
for (const [name, expr] of Object.entries(assertions))s.addAndTrack(expr, name);
|
|
284
|
+
const report = async (model)=>{
|
|
285
|
+
const out = [];
|
|
286
|
+
for (const seg of spec.segments){
|
|
287
|
+
for(let i = 0; i < allSels[seg.id].length; i++){
|
|
288
|
+
const v = await maybeAwait(model.eval(allSels[seg.id][i], true));
|
|
289
|
+
if (String(v) !== 'true') continue;
|
|
290
|
+
const o = seg.options[i];
|
|
291
|
+
out.push({
|
|
292
|
+
leg: seg.id,
|
|
293
|
+
...evaluateOptionMove(seg.id, o.move)
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return out;
|
|
299
|
+
};
|
|
300
|
+
async function maybeAwait(v) {
|
|
301
|
+
return v instanceof Promise ? await v : v;
|
|
302
|
+
}
|
|
303
|
+
if (String(await s.check()) !== 'unsat') {
|
|
304
|
+
const legs = await report(s.model());
|
|
305
|
+
const money = legs.reduce((a, l)=>a + l.price_cny, 0);
|
|
306
|
+
const redFlags = legs.filter((l)=>spec.segments.find((sg)=>sg.id === l.leg)?.options.some((o)=>o.move?.redEye) && l.energy_pct < 50).map((l)=>i18nT('un.redflag_redeye', {
|
|
307
|
+
leg: l.leg,
|
|
308
|
+
pct: l.energy_pct
|
|
309
|
+
}));
|
|
310
|
+
return {
|
|
311
|
+
feasible: true,
|
|
312
|
+
money_cny: money,
|
|
313
|
+
legs,
|
|
314
|
+
red_flags: redFlags,
|
|
315
|
+
work_window_exclusions: exclusions,
|
|
316
|
+
skeleton_notes: skeletonNotes.length ? skeletonNotes : undefined
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const core = coreOf(s).sort();
|
|
320
|
+
const suggestions = [];
|
|
321
|
+
for (const name of core){
|
|
322
|
+
const s2 = new Solver();
|
|
323
|
+
for (const sel of Object.values(allSels))s2.add(exactlyOne(sel));
|
|
324
|
+
for (const [n2, expr] of Object.entries(assertions)){
|
|
325
|
+
if (n2 !== name) s2.addAndTrack(expr, n2);
|
|
326
|
+
}
|
|
327
|
+
if (String(await s2.check()) !== 'unsat') {
|
|
328
|
+
const legs = await report(s2.model());
|
|
329
|
+
suggestions.push({
|
|
330
|
+
relax: name,
|
|
331
|
+
money_cny: legs.reduce((a, l)=>a + l.price_cny, 0)
|
|
295
332
|
});
|
|
296
|
-
break;
|
|
297
333
|
}
|
|
298
334
|
}
|
|
299
|
-
return out;
|
|
300
|
-
};
|
|
301
|
-
async function maybeAwait(v) {
|
|
302
|
-
return v instanceof Promise ? await v : v;
|
|
303
|
-
}
|
|
304
|
-
if (String(await s.check()) !== 'unsat') {
|
|
305
|
-
const legs = await report(s.model());
|
|
306
|
-
const money = legs.reduce((a, l)=>a + l.price_cny, 0);
|
|
307
|
-
const redFlags = legs.filter((l)=>spec.segments.find((sg)=>sg.id === l.leg)?.options.some((o)=>o.move?.redEye) && l.energy_pct < 50).map((l)=>`${l.leg} 落地精力仅 ${l.energy_pct}%(红眼后直奔事务,当日不宜安排重要会议)`);
|
|
308
335
|
return {
|
|
309
|
-
feasible:
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
red_flags: redFlags,
|
|
313
|
-
work_window_exclusions: exclusions,
|
|
314
|
-
skeleton_notes: skeletonNotes.length ? skeletonNotes : undefined
|
|
336
|
+
feasible: false,
|
|
337
|
+
unsat_core: core,
|
|
338
|
+
suggestions
|
|
315
339
|
};
|
|
316
|
-
}
|
|
317
|
-
const core = coreOf(s).sort();
|
|
318
|
-
const suggestions = [];
|
|
319
|
-
for (const name of core){
|
|
320
|
-
const s2 = new Solver();
|
|
321
|
-
for (const sel of Object.values(allSels))s2.add(exactlyOne(sel));
|
|
322
|
-
for (const [n2, expr] of Object.entries(assertions)){
|
|
323
|
-
if (n2 !== name) s2.addAndTrack(expr, n2);
|
|
324
|
-
}
|
|
325
|
-
if (String(await s2.check()) !== 'unsat') {
|
|
326
|
-
const legs = await report(s2.model());
|
|
327
|
-
suggestions.push({
|
|
328
|
-
relax: name,
|
|
329
|
-
money_cny: legs.reduce((a, l)=>a + l.price_cny, 0)
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
feasible: false,
|
|
335
|
-
unsat_core: core,
|
|
336
|
-
suggestions
|
|
337
|
-
};
|
|
340
|
+
});
|
|
338
341
|
}
|
|
339
342
|
function checkTrueCost(t, spec) {
|
|
340
343
|
const fails = [];
|
|
@@ -459,7 +462,10 @@ export function solveChoiceSegment(spec, req) {
|
|
|
459
462
|
wish = {
|
|
460
463
|
name: opt.label,
|
|
461
464
|
conditions: conds,
|
|
462
|
-
reason:
|
|
465
|
+
reason: i18nT('sg.wish_reason_window', {
|
|
466
|
+
days: windowDays,
|
|
467
|
+
minDays
|
|
468
|
+
})
|
|
463
469
|
};
|
|
464
470
|
}
|
|
465
471
|
verdicts.push({
|
|
@@ -483,52 +489,99 @@ function renderCandidateMarkdown(req, verdicts, recommended) {
|
|
|
483
489
|
const feasible = verdicts.filter((v)=>v['feasible']).sort((a, b)=>b['imagery_match'] - a['imagery_match']);
|
|
484
490
|
const parked = verdicts.filter((v)=>!v['feasible']);
|
|
485
491
|
const lines = [];
|
|
486
|
-
lines.push(
|
|
487
|
-
|
|
492
|
+
lines.push(i18nT('md.header', {
|
|
493
|
+
note: req.note
|
|
494
|
+
}));
|
|
495
|
+
lines.push(i18nT('md.constraints', {
|
|
496
|
+
days: req.windowDays,
|
|
497
|
+
budget: req.budgetCny,
|
|
498
|
+
hours: requiredUsableHours(req.motivation).toFixed(1)
|
|
499
|
+
}));
|
|
488
500
|
lines.push('');
|
|
489
501
|
for (const v of parked){
|
|
490
502
|
const core = v['unsat_core'] ?? [];
|
|
491
|
-
lines.push(
|
|
503
|
+
lines.push(i18nT('md.infeasible', {
|
|
504
|
+
name: String(v['name']),
|
|
505
|
+
core: core.join('、')
|
|
506
|
+
}));
|
|
492
507
|
const sug = (v['suggestions'] ?? [])[0];
|
|
493
|
-
if (sug) lines.push(
|
|
508
|
+
if (sug) lines.push(i18nT('md.relax_one', {
|
|
509
|
+
relax: String(sug['relax']),
|
|
510
|
+
money: String(sug['resulting_money_cny'] ?? '')
|
|
511
|
+
}));
|
|
494
512
|
const wish = v['wish_pool'];
|
|
495
513
|
if (wish) {
|
|
496
514
|
const conds = wish['conditions'];
|
|
497
|
-
const budgetNote = conds['budget_cny'] ?
|
|
515
|
+
const budgetNote = conds['budget_cny'] ? i18nT('md.wish_budget', {
|
|
516
|
+
cny: String(conds['budget_cny'])
|
|
517
|
+
}) : '';
|
|
498
518
|
const months = conds['best_months'];
|
|
499
|
-
const season = months ?
|
|
500
|
-
|
|
519
|
+
const season = months ? i18nT('md.wish_season', {
|
|
520
|
+
months: String(months)
|
|
521
|
+
}) : '';
|
|
522
|
+
lines.push(i18nT('md.wish_pool', {
|
|
523
|
+
days: String(conds['days']),
|
|
524
|
+
budgetNote,
|
|
525
|
+
season
|
|
526
|
+
}));
|
|
501
527
|
}
|
|
502
528
|
}
|
|
503
529
|
lines.push('');
|
|
504
530
|
for (const v of feasible){
|
|
505
531
|
const ch = v['chosen'];
|
|
506
|
-
const
|
|
507
|
-
lines.push(
|
|
532
|
+
const tc = v['true_cost'];
|
|
533
|
+
lines.push(i18nT('md.feasible_unified', {
|
|
534
|
+
name: String(v['name']),
|
|
535
|
+
svc: String(ch['out_service']),
|
|
536
|
+
mode: String(ch['out_transfer']),
|
|
537
|
+
wake: String(tc['wake']),
|
|
538
|
+
energy: String(tc['energy_arrival_pct']),
|
|
539
|
+
d2d: String(tc['door_to_door_out']),
|
|
540
|
+
hours: String(tc['usable_hours']),
|
|
541
|
+
money: String(tc['money_cny'])
|
|
542
|
+
}));
|
|
508
543
|
}
|
|
509
544
|
if (feasible.length) {
|
|
510
545
|
lines.push('');
|
|
511
546
|
const best = feasible[0];
|
|
512
547
|
const alt = feasible[1];
|
|
513
|
-
lines.push(
|
|
548
|
+
lines.push(i18nT('md.recommend', {
|
|
549
|
+
best: String(best['name']),
|
|
550
|
+
match: (best['imagery_match'] * 100).toFixed(0)
|
|
551
|
+
}) + (alt ? i18nT('md.alt', {
|
|
552
|
+
name: String(alt['name']),
|
|
553
|
+
money: String(alt['true_cost']['money_cny']),
|
|
554
|
+
match: (alt['imagery_match'] * 100).toFixed(0)
|
|
555
|
+
}) : ''));
|
|
514
556
|
}
|
|
515
557
|
lines.push('');
|
|
516
|
-
lines.push('
|
|
558
|
+
lines.push(i18nT('md.decide_two'));
|
|
517
559
|
if (feasible.length >= 2) {
|
|
518
|
-
lines.push(
|
|
560
|
+
lines.push(i18nT('md.q_choice', {
|
|
561
|
+
a: String(feasible[0]['name']),
|
|
562
|
+
b: String(feasible[1]['name'])
|
|
563
|
+
}));
|
|
519
564
|
} else if (feasible.length === 1) {
|
|
520
|
-
lines.push(
|
|
565
|
+
lines.push(i18nT('md.q_single', {
|
|
566
|
+
name: String(feasible[0]['name'])
|
|
567
|
+
}));
|
|
521
568
|
} else {
|
|
522
|
-
lines.push('
|
|
569
|
+
lines.push(i18nT('md.q_none'));
|
|
523
570
|
}
|
|
524
571
|
const p0 = parked[0];
|
|
525
572
|
const p0wish = p0?.['wish_pool'];
|
|
526
573
|
if (p0wish) {
|
|
527
574
|
const conds = p0wish['conditions'];
|
|
528
|
-
lines.push(
|
|
575
|
+
lines.push(i18nT('md.q_wish', {
|
|
576
|
+
name: String(p0['name']),
|
|
577
|
+
days: String(conds['days'])
|
|
578
|
+
}));
|
|
529
579
|
} else if (feasible.length) {
|
|
530
580
|
const ch = feasible[0]['chosen'];
|
|
531
|
-
lines.push(
|
|
581
|
+
lines.push(i18nT('md.q_depart', {
|
|
582
|
+
id: String(ch['out_service']),
|
|
583
|
+
dep: ''
|
|
584
|
+
}));
|
|
532
585
|
}
|
|
533
586
|
return lines.join('\n');
|
|
534
587
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
let z3Promise = null;
|
|
2
|
+
let chain = Promise.resolve();
|
|
3
|
+
export async function getZ3() {
|
|
4
|
+
if (!z3Promise) {
|
|
5
|
+
const { init } = await import('z3-solver');
|
|
6
|
+
z3Promise = (async ()=>(await init()).Context('main'))();
|
|
7
|
+
}
|
|
8
|
+
return z3Promise;
|
|
9
|
+
}
|
|
10
|
+
export async function withZ3(label, session) {
|
|
11
|
+
const z3 = await getZ3();
|
|
12
|
+
const turn = chain.then(()=>session(z3));
|
|
13
|
+
chain = turn.then(()=>undefined, ()=>undefined);
|
|
14
|
+
return turn;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
//# sourceURL=/Users/bytedance/work/gotry/ts/src/z3-shared.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danceiny/gotry",
|
|
3
|
-
"version": "0.0.1-rc.
|
|
3
|
+
"version": "0.0.1-rc.12",
|
|
4
4
|
"description": "GoTry — 从出发到下一次出发的 AI 旅行 Agent(dsh 插件)。npm 包入口 + vendored dsh runtime + 5 行 README 安装路径。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "ts/src/index.ts",
|
|
@@ -47,7 +47,9 @@
|
|
|
47
47
|
"ts/package.json",
|
|
48
48
|
"cordis.gotry-patch.yml",
|
|
49
49
|
"README.md",
|
|
50
|
-
"LICENSE"
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"ts/capabilities/session-consent.ts",
|
|
52
|
+
"ts/capabilities/session-login.ts"
|
|
51
53
|
],
|
|
52
54
|
"engines": {
|
|
53
55
|
"node": ">=22.0.0"
|
|
@@ -81,4 +83,4 @@
|
|
|
81
83
|
"deepseek-harness"
|
|
82
84
|
],
|
|
83
85
|
"license": "MIT"
|
|
84
|
-
}
|
|
86
|
+
}
|