dsh-lite-memory 0.18.43 → 0.18.45
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/client.js +5 -3
- package/docs/BENCH.md +14 -0
- package/docs/RELEASE-0.18.44.md +21 -0
- package/docs/RELEASE-0.18.45.md +16 -0
- package/package.json +3 -3
- package/test/anchor.test.mjs +0 -795
- package/test/client.test.mjs +0 -400
- package/test/contract.test.mjs +0 -1342
- package/test/control.test.mjs +0 -324
- package/test/crossing.test.mjs +0 -471
- package/test/eval-seed.test.mjs +0 -113
- package/test/evolution-read.test.mjs +0 -658
- package/test/fixtures/legacy-v1/archive/mem_444444444444.json +0 -18
- package/test/fixtures/legacy-v1/entries/mem_111111111111.md +0 -2
- package/test/fixtures/legacy-v1/entries/mem_222222222222.md +0 -1
- package/test/fixtures/legacy-v1/entries/mem_333333333333.md +0 -1
- package/test/fixtures/legacy-v1/index.json +0 -63
- package/test/ha.test.mjs +0 -186
- package/test/layer-read.test.mjs +0 -293
- package/test/learning.test.mjs +0 -1450
- package/test/lexical.test.mjs +0 -68
- package/test/palace.test.mjs +0 -104
- package/test/recall.test.mjs +0 -1039
- package/test/record.test.mjs +0 -130
- package/test/resident.test.mjs +0 -46
- package/test/runtime-context.test.mjs +0 -192
- package/test/safety.test.mjs +0 -119
- package/test/scenario.test.mjs +0 -184
- package/test/state.test.mjs +0 -663
- package/test/store.test.mjs +0 -1487
- package/test/view.test.mjs +0 -264
package/test/anchor.test.mjs
DELETED
|
@@ -1,795 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import * as anchor from '../anchor.js';
|
|
4
|
-
import * as view from '../view.js';
|
|
5
|
-
|
|
6
|
-
const base = () => ({
|
|
7
|
-
projectId: '/work/demo',
|
|
8
|
-
sessionId: 's1',
|
|
9
|
-
messageIds: ['m1'],
|
|
10
|
-
query: '帮我把这期视频的开头改快一点',
|
|
11
|
-
registeredSeries: ['口播', 'Vlog'],
|
|
12
|
-
now: Date.now(),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test('R01 首个自由任务只建 unbound 作品壳,读取只给索引', () => {
|
|
16
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), base());
|
|
17
|
-
assert.equal(routed.anchor.status, 'unbound');
|
|
18
|
-
assert.equal(routed.anchor.taskKind, undefined);
|
|
19
|
-
assert.equal(routed.anchor.seriesId, undefined);
|
|
20
|
-
const decision = anchor.decideRead({
|
|
21
|
-
mode: 'adaptive', enabled: true, identityConfirmed: true,
|
|
22
|
-
anchor: routed.anchor, ranges: ['口播', 'Vlog', '访谈'],
|
|
23
|
-
});
|
|
24
|
-
assert.equal(decision.kind, 'index');
|
|
25
|
-
assert.deepEqual(decision.ranges, ['口播', 'Vlog', '访谈']);
|
|
26
|
-
assert.ok(!('rules' in decision));
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('R02 前台选择只能把锚点升为 proposed,不冒充 active', () => {
|
|
30
|
-
const first = anchor.routeTask(anchor.emptyTasks(), base());
|
|
31
|
-
const selected = anchor.applySelection(first.tasks, {
|
|
32
|
-
sessionId: 's1', batchKey: first.anchor.batchKey, messageIds: ['m1'],
|
|
33
|
-
selection: { seriesId: '口播' }, now: base().now,
|
|
34
|
-
});
|
|
35
|
-
assert.equal(selected.anchor.status, 'proposed');
|
|
36
|
-
assert.equal(selected.anchor.seriesId, '口播');
|
|
37
|
-
assert.equal(selected.episode.status, 'proposed');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('R03 再做一期创建新 episode,继承系列并回到 brief', () => {
|
|
41
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
42
|
-
...base(), selection: { seriesId: '口播' },
|
|
43
|
-
});
|
|
44
|
-
const next = anchor.routeTask(first.tasks, {
|
|
45
|
-
...base(), messageIds: ['m2'], query: '再做一期口播,讲同一套开场',
|
|
46
|
-
});
|
|
47
|
-
assert.equal(next.decision.kind, 'continuation');
|
|
48
|
-
assert.notEqual(next.anchor.episodeId, first.anchor.episodeId);
|
|
49
|
-
assert.equal(next.anchor.seriesId, '口播');
|
|
50
|
-
assert.equal(next.anchor.phase, 'brief');
|
|
51
|
-
assert.equal(next.episode.previousEpisodeId, first.anchor.episodeId);
|
|
52
|
-
assert.equal(next.episode.status, 'continuable');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('R04 新一期不继承上一期的主题、脚本和临时例外', () => {
|
|
56
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
57
|
-
...base(), selection: { seriesId: '口播' },
|
|
58
|
-
});
|
|
59
|
-
const next = anchor.routeTask(first.tasks, {
|
|
60
|
-
...base(), messageIds: ['m2'], query: '再来一期',
|
|
61
|
-
});
|
|
62
|
-
for (const key of ['theme', 'script', 'client', 'music', 'exceptions']) {
|
|
63
|
-
assert.equal(next.anchor[key], undefined, `${key} 不应被继承`);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('R05 新会话有明确项目默认时建立 active 锚点', () => {
|
|
68
|
-
const tasks = anchor.setDefault(anchor.emptyTasks(), { seriesId: '口播', taskKind: 'video' });
|
|
69
|
-
const routed = anchor.routeTask(tasks, { ...base(), sessionId: 's2' });
|
|
70
|
-
assert.equal(routed.anchor.status, 'active');
|
|
71
|
-
assert.equal(routed.anchor.seriesId, '口播');
|
|
72
|
-
assert.equal(routed.anchor.taskKind, 'video');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('R06 明确改做别的任务时退出旧焦点,不把视频规则带过去', () => {
|
|
76
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
77
|
-
...base(), selection: { seriesId: '口播' },
|
|
78
|
-
});
|
|
79
|
-
const switched = anchor.routeTask(first.tasks, {
|
|
80
|
-
...base(), messageIds: ['m2'], query: '改做 README 和计费说明',
|
|
81
|
-
});
|
|
82
|
-
assert.equal(switched.anchor.episodeId === first.anchor.episodeId, false);
|
|
83
|
-
assert.equal(switched.anchor.seriesId, undefined);
|
|
84
|
-
assert.equal(switched.anchor.status, 'unbound');
|
|
85
|
-
assert.equal(switched.decision.switched, true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('R07 只说谢谢不注入规则,但保留后续可续指针', () => {
|
|
89
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
90
|
-
...base(), selection: { seriesId: '口播' },
|
|
91
|
-
});
|
|
92
|
-
const thanks = anchor.routeTask(first.tasks, { ...base(), messageIds: ['m2'], query: '谢谢!' });
|
|
93
|
-
assert.equal(thanks.decision.kind, 'none');
|
|
94
|
-
assert.equal(thanks.decision.pointer, true);
|
|
95
|
-
const decision = anchor.decideRead({
|
|
96
|
-
mode: 'adaptive', enabled: true, identityConfirmed: true,
|
|
97
|
-
anchor: thanks.anchor, ranges: ['口播'], thanks: true,
|
|
98
|
-
});
|
|
99
|
-
assert.equal(decision.kind, 'none');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('R08 未知阶段的条件只算 needs_context,不当成适用', () => {
|
|
103
|
-
const record = {
|
|
104
|
-
id: 'mem_x', title: '口播开头', applicability: 'conditional',
|
|
105
|
-
conditions: [{ key: 'seriesId', value: '口播' }, { key: 'phase', value: 'editing' }],
|
|
106
|
-
};
|
|
107
|
-
const context = view.trustedContext({ cwd: '/work/demo' }, { seriesId: '口播' });
|
|
108
|
-
const evaluated = view.evaluate(record, context);
|
|
109
|
-
assert.equal(evaluated.state, 'unknown');
|
|
110
|
-
assert.deepEqual(evaluated.missing, ['phase']);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('R09 任务条件键可由已确认绑定进入可信上下文', () => {
|
|
114
|
-
const context = view.trustedContext({ cwd: '/work/demo' }, {
|
|
115
|
-
taskKind: 'video', seriesId: '口播', phase: 'brief',
|
|
116
|
-
});
|
|
117
|
-
assert.equal(context.taskKind, 'video');
|
|
118
|
-
assert.equal(context.seriesId, '口播');
|
|
119
|
-
assert.equal(context.phase, 'brief');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test('R10 同一批次重放只生成一个 episode', () => {
|
|
123
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
124
|
-
...base(), selection: { seriesId: '口播' },
|
|
125
|
-
});
|
|
126
|
-
const again = anchor.routeTask(first.tasks, {
|
|
127
|
-
...base(), selection: { seriesId: '口播' },
|
|
128
|
-
});
|
|
129
|
-
assert.equal(again.anchor.episodeId, first.anchor.episodeId);
|
|
130
|
-
assert.equal(Object.keys(again.tasks.episodes).length, 1);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('R10b 同一延续请求重试只生成一个新 episode', () => {
|
|
134
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
135
|
-
...base(), selection: { seriesId: '口播' },
|
|
136
|
-
});
|
|
137
|
-
const a = anchor.routeTask(first.tasks, { ...base(), messageIds: ['m2', 'm3'], query: '再做一期' });
|
|
138
|
-
const b = anchor.routeTask(a.tasks, { ...base(), messageIds: ['m3', 'm2'], query: '再做一期' });
|
|
139
|
-
assert.equal(b.anchor.episodeId, a.anchor.episodeId);
|
|
140
|
-
assert.equal(Object.keys(b.tasks.episodes).length, 2);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test('R11 请求批次只由本轮新人类消息决定', () => {
|
|
144
|
-
assert.equal(anchor.requestBatchKey(['b', 'a']), anchor.requestBatchKey(['a', 'b', 'a']));
|
|
145
|
-
assert.notEqual(anchor.requestBatchKey(['a']), anchor.requestBatchKey(['a', 'b']));
|
|
146
|
-
assert.equal(anchor.requestBatchKey([]), '');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('R12 contextId 带项目摘要,跨项目直接识别', () => {
|
|
150
|
-
const id = anchor.contextIdFor('/work/demo', 'rb_1', 'n');
|
|
151
|
-
assert.equal(anchor.projectTagOf(id), anchor.projectTag('/work/demo'));
|
|
152
|
-
assert.notEqual(anchor.projectTagOf(id), anchor.projectTag('/work/other'));
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('R13 未绑定的索引最多三个栏目且不超过 600 字节', () => {
|
|
156
|
-
const decision = anchor.decideRead({
|
|
157
|
-
mode: 'adaptive', enabled: true, identityConfirmed: true, anchor: { status: 'unbound' },
|
|
158
|
-
ranges: ['口播', 'Vlog', '访谈', '广告'],
|
|
159
|
-
});
|
|
160
|
-
assert.equal(decision.ranges.length, 3);
|
|
161
|
-
assert.ok(Buffer.byteLength(decision.ranges.join('、'), 'utf8') <= anchor.TASK_LIMITS.indexBytes);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test('R14 实验关闭与身份未确认都不读', () => {
|
|
165
|
-
assert.equal(anchor.decideRead({ mode: 'adaptive', enabled: false, identityConfirmed: true, anchor: { status: 'active' } }).kind, 'none');
|
|
166
|
-
assert.equal(anchor.decideRead({ mode: 'adaptive', enabled: true, identityConfirmed: false, anchor: { status: 'active' } }).kind, 'none');
|
|
167
|
-
assert.equal(anchor.decideRead({ mode: 'off', enabled: true, identityConfirmed: true, anchor: { status: 'active' } }).kind, 'none');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test('R15 proposed 自动路径只给索引,前台绑定后才给规则', () => {
|
|
171
|
-
const auto = anchor.decideRead({
|
|
172
|
-
mode: 'adaptive', enabled: true, identityConfirmed: true,
|
|
173
|
-
anchor: { status: 'proposed', seriesId: '口播' }, ranges: ['口播'],
|
|
174
|
-
});
|
|
175
|
-
assert.equal(auto.kind, 'index');
|
|
176
|
-
const bound = anchor.decideRead({
|
|
177
|
-
mode: 'adaptive', enabled: true, identityConfirmed: true, boundForRequest: true,
|
|
178
|
-
anchor: { status: 'proposed', seriesId: '口播' }, ranges: ['口播'],
|
|
179
|
-
});
|
|
180
|
-
assert.equal(bound.kind, 'rules');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test('R16 会话指针硬上限 8,超出按最近更新保留', () => {
|
|
184
|
-
let tasks = anchor.emptyTasks();
|
|
185
|
-
for (let i = 0; i < 12; i += 1) {
|
|
186
|
-
const routed = anchor.routeTask(tasks, {
|
|
187
|
-
...base(), sessionId: `s${i}`, messageIds: [`m${i}`], now: base().now + i * 1000,
|
|
188
|
-
});
|
|
189
|
-
tasks = routed.tasks;
|
|
190
|
-
}
|
|
191
|
-
const normalized = anchor.normalizeTasks(tasks);
|
|
192
|
-
assert.equal(Object.keys(normalized.sessions).length, 8);
|
|
193
|
-
assert.equal(normalized.sessions.s11.seriesId, undefined);
|
|
194
|
-
assert.equal(normalized.sessions.s0, undefined);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test('R17 切换任务后旧 episode 不再被当前会话引用', () => {
|
|
198
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
199
|
-
...base(), selection: { seriesId: '口播' },
|
|
200
|
-
});
|
|
201
|
-
const switched = anchor.routeTask(first.tasks, {
|
|
202
|
-
...base(), messageIds: ['m2'], query: '换个任务,做代码审查',
|
|
203
|
-
});
|
|
204
|
-
assert.notEqual(switched.anchor.episodeId, first.anchor.episodeId);
|
|
205
|
-
assert.equal(switched.tasks.sessions.s1.episodeId, switched.anchor.episodeId);
|
|
206
|
-
assert.equal(switched.tasks.episodes[first.anchor.episodeId].status, 'proposed');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('R18 自由措辞不能把未注册栏目变成已绑定', () => {
|
|
210
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), {
|
|
211
|
-
...base(), query: '做一期美食节目', registeredSeries: ['口播', 'Vlog'],
|
|
212
|
-
});
|
|
213
|
-
assert.equal(routed.anchor.status, 'unbound');
|
|
214
|
-
assert.equal(routed.anchor.seriesId, undefined);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test('R12 无切换词的新任务撤销旧作品绑定,不继承栏目和规则', () => {
|
|
218
|
-
const sessionId = 'sess-r12';
|
|
219
|
-
let tasks = anchor.emptyTasks();
|
|
220
|
-
const first = anchor.routeTask(tasks, {
|
|
221
|
-
sessionId,
|
|
222
|
-
batchKey: 'rb_first',
|
|
223
|
-
messageIds: ['m1'],
|
|
224
|
-
query: '帮我把这期口播视频的开头改一下',
|
|
225
|
-
selection: { seriesId: '口播', taskKind: '口播视频' },
|
|
226
|
-
now: 1000,
|
|
227
|
-
});
|
|
228
|
-
assert.equal(first.anchor.status, 'proposed');
|
|
229
|
-
const bound = anchor.applySelection(first.tasks, { sessionId, selection: { seriesId: '口播' }, batchKey: 'rb_first', messageIds: ['m1'], now: 1500 });
|
|
230
|
-
assert.equal(bound.anchor.status, 'proposed');
|
|
231
|
-
const active = bound.tasks.sessions[sessionId];
|
|
232
|
-
active.status = 'active';
|
|
233
|
-
tasks = bound.tasks;
|
|
234
|
-
const next = anchor.routeTask(tasks, {
|
|
235
|
-
sessionId,
|
|
236
|
-
batchKey: 'rb_next',
|
|
237
|
-
messageIds: ['m2'],
|
|
238
|
-
query: '帮我排查 PostgreSQL 连接超时',
|
|
239
|
-
registeredSeries: ['口播'],
|
|
240
|
-
now: 2000,
|
|
241
|
-
});
|
|
242
|
-
assert.equal(next.anchor.status, 'unbound');
|
|
243
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
244
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
245
|
-
assert.ok(next.episode.previousEpisodeId);
|
|
246
|
-
|
|
247
|
-
/** 新任务已撤销旧绑定:旧栏目规则不能再自动进入这一步。 */
|
|
248
|
-
const read = anchor.decideRead({
|
|
249
|
-
enabled: true,
|
|
250
|
-
identityConfirmed: true,
|
|
251
|
-
anchor: next.anchor,
|
|
252
|
-
ranges: [{ seriesId: '口播', taskKind: '口播视频', reason: '旧任务' }],
|
|
253
|
-
});
|
|
254
|
-
assert.equal(read.kind, 'index');
|
|
255
|
-
assert.equal(read.rules, undefined);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test('R13 继续改/接着做仍继承同一任务', () => {
|
|
259
|
-
let tasks = anchor.emptyTasks();
|
|
260
|
-
const first = anchor.routeTask(tasks, {
|
|
261
|
-
sessionId: 'sess-r13',
|
|
262
|
-
batchKey: 'rb_a',
|
|
263
|
-
messageIds: ['m1'],
|
|
264
|
-
query: '帮我把这期口播视频的开头改一下',
|
|
265
|
-
selection: { seriesId: '口播' },
|
|
266
|
-
now: 1000,
|
|
267
|
-
});
|
|
268
|
-
tasks = first.tasks;
|
|
269
|
-
const next = anchor.routeTask(tasks, {
|
|
270
|
-
sessionId: 'sess-r13',
|
|
271
|
-
batchKey: 'rb_b',
|
|
272
|
-
messageIds: ['m2'],
|
|
273
|
-
query: '继续改,节奏再快一点',
|
|
274
|
-
now: 2000,
|
|
275
|
-
});
|
|
276
|
-
assert.equal(next.anchor.status, 'continuable');
|
|
277
|
-
assert.equal(next.decision.kind, 'continuation');
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('R14 无候选栏目时返回 none,不再要求选栏目', () => {
|
|
281
|
-
const read = anchor.decideRead({ enabled: true, identityConfirmed: true, anchor: null, ranges: [] });
|
|
282
|
-
assert.equal(read.kind, 'none');
|
|
283
|
-
assert.equal(read.reason, 'no_range');
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
/** 已绑定的口播任务:用于验证换任务时旧范围确实被撤销。 */
|
|
287
|
-
function activeVideoSession() {
|
|
288
|
-
const first = anchor.routeTask(anchor.emptyTasks(), {
|
|
289
|
-
sessionId: 'sess-switch',
|
|
290
|
-
batchKey: 'rb_video',
|
|
291
|
-
messageIds: ['m1'],
|
|
292
|
-
query: '帮我把这期口播视频的开头改一下',
|
|
293
|
-
selection: { seriesId: '口播', taskKind: '口播视频' },
|
|
294
|
-
now: 1000,
|
|
295
|
-
});
|
|
296
|
-
const anchorState = first.tasks.sessions['sess-switch'];
|
|
297
|
-
anchorState.status = 'active';
|
|
298
|
-
return first.tasks;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
test('R20 带「继续」但出现新对象时,先撤销旧范围', () => {
|
|
302
|
-
const tasks = activeVideoSession();
|
|
303
|
-
const next = anchor.routeTask(tasks, {
|
|
304
|
-
sessionId: 'sess-switch',
|
|
305
|
-
batchKey: 'rb_pg_continue',
|
|
306
|
-
messageIds: ['m2'],
|
|
307
|
-
query: '接下来继续排查 PostgreSQL 连接超时',
|
|
308
|
-
registeredSeries: ['口播'],
|
|
309
|
-
now: 2000,
|
|
310
|
-
});
|
|
311
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
312
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
313
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
314
|
-
assert.equal(next.episode.previousEpisodeId, tasks.sessions['sess-switch'].episodeId);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
test('R21 带「这个」但出现新对象时,不沿用旧范围', () => {
|
|
318
|
-
const tasks = activeVideoSession();
|
|
319
|
-
const next = anchor.routeTask(tasks, {
|
|
320
|
-
sessionId: 'sess-switch',
|
|
321
|
-
batchKey: 'rb_pg_this',
|
|
322
|
-
messageIds: ['m2'],
|
|
323
|
-
query: '现在帮我排查这个 PostgreSQL 连接超时',
|
|
324
|
-
registeredSeries: ['口播'],
|
|
325
|
-
now: 2000,
|
|
326
|
-
});
|
|
327
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
328
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
329
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
test('R22 真正的续改保留原任务范围', () => {
|
|
333
|
-
const tasks = activeVideoSession();
|
|
334
|
-
const next = anchor.routeTask(tasks, {
|
|
335
|
-
sessionId: 'sess-switch',
|
|
336
|
-
batchKey: 'rb_video_continue',
|
|
337
|
-
messageIds: ['m2'],
|
|
338
|
-
query: '继续改刚才那版脚本,开头再直接一点',
|
|
339
|
-
registeredSeries: ['口播'],
|
|
340
|
-
now: 2000,
|
|
341
|
-
});
|
|
342
|
-
assert.equal(next.decision.kind, 'continuation');
|
|
343
|
-
assert.equal(next.anchor.seriesId, '口播');
|
|
344
|
-
assert.equal(next.episode.previousEpisodeId, tasks.sessions['sess-switch'].episodeId);
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
test('R22b 短续句「继续优化收敛」不点名新对象,仍算续作', () => {
|
|
348
|
-
const tasks = activeVideoSession();
|
|
349
|
-
const next = anchor.routeTask(tasks, {
|
|
350
|
-
sessionId: 'sess-switch',
|
|
351
|
-
batchKey: 'rb_ellipse',
|
|
352
|
-
messageIds: ['m2'],
|
|
353
|
-
query: '继续优化收敛',
|
|
354
|
-
registeredSeries: ['口播'],
|
|
355
|
-
now: 2000,
|
|
356
|
-
});
|
|
357
|
-
assert.equal(next.decision.kind, 'continuation');
|
|
358
|
-
assert.equal(next.anchor.seriesId, '口播');
|
|
359
|
-
assert.equal(next.episode.previousEpisodeId, tasks.sessions['sess-switch'].episodeId);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
test('R22c 短续句点名新对象时仍然换任务', () => {
|
|
363
|
-
const tasks = activeVideoSession();
|
|
364
|
-
const next = anchor.routeTask(tasks, {
|
|
365
|
-
sessionId: 'sess-switch',
|
|
366
|
-
batchKey: 'rb_ellipse_obj',
|
|
367
|
-
messageIds: ['m2'],
|
|
368
|
-
query: '继续优化数据库连接池',
|
|
369
|
-
registeredSeries: ['口播'],
|
|
370
|
-
now: 2000,
|
|
371
|
-
});
|
|
372
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
373
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
test('R23 只指代旧稿的局部修改不被当成换任务', () => {
|
|
377
|
-
const tasks = activeVideoSession();
|
|
378
|
-
const next = anchor.routeTask(tasks, {
|
|
379
|
-
sessionId: 'sess-switch',
|
|
380
|
-
batchKey: 'rb_video_tweak',
|
|
381
|
-
messageIds: ['m2'],
|
|
382
|
-
query: '现在把开头改短一点',
|
|
383
|
-
registeredSeries: ['口播'],
|
|
384
|
-
now: 2000,
|
|
385
|
-
});
|
|
386
|
-
assert.equal(next.decision.kind, 'focus');
|
|
387
|
-
assert.equal(next.anchor.seriesId, '口播');
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
test('R24 继续做新领域的中文任务也算换任务', () => {
|
|
391
|
-
const tasks = activeVideoSession();
|
|
392
|
-
const next = anchor.routeTask(tasks, {
|
|
393
|
-
sessionId: 'sess-switch',
|
|
394
|
-
batchKey: 'rb_research',
|
|
395
|
-
messageIds: ['m2'],
|
|
396
|
-
query: '继续看看竞品定价',
|
|
397
|
-
registeredSeries: ['口播'],
|
|
398
|
-
now: 2000,
|
|
399
|
-
});
|
|
400
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
401
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
test('R25 单独说「继续」不沿用旧任务范围', () => {
|
|
405
|
-
const tasks = activeVideoSession();
|
|
406
|
-
const previous = tasks.sessions['sess-switch'];
|
|
407
|
-
const next = anchor.routeTask(tasks, {
|
|
408
|
-
sessionId: 'sess-switch',
|
|
409
|
-
batchKey: 'rb_weak_continue',
|
|
410
|
-
messageIds: ['m2'],
|
|
411
|
-
query: '继续',
|
|
412
|
-
registeredSeries: ['口播'],
|
|
413
|
-
now: 2000,
|
|
414
|
-
});
|
|
415
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
416
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
417
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
418
|
-
assert.equal(next.episode.previousEpisodeId, previous.episodeId);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
test('R26 单独说「这个」不沿用旧任务范围', () => {
|
|
422
|
-
const tasks = activeVideoSession();
|
|
423
|
-
const next = anchor.routeTask(tasks, {
|
|
424
|
-
sessionId: 'sess-switch',
|
|
425
|
-
batchKey: 'rb_weak_this',
|
|
426
|
-
messageIds: ['m2'],
|
|
427
|
-
query: '这个',
|
|
428
|
-
registeredSeries: ['口播'],
|
|
429
|
-
now: 2000,
|
|
430
|
-
});
|
|
431
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
432
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
433
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
test('R27 单独说「之前」不沿用旧任务范围', () => {
|
|
437
|
-
const tasks = activeVideoSession();
|
|
438
|
-
const next = anchor.routeTask(tasks, {
|
|
439
|
-
sessionId: 'sess-switch',
|
|
440
|
-
batchKey: 'rb_weak_before',
|
|
441
|
-
messageIds: ['m2'],
|
|
442
|
-
query: '之前',
|
|
443
|
-
registeredSeries: ['口播'],
|
|
444
|
-
now: 2000,
|
|
445
|
-
});
|
|
446
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
447
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
448
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
test('R28 指代加动作的续接仍保留旧任务范围', () => {
|
|
452
|
-
const tasks = activeVideoSession();
|
|
453
|
-
const previous = tasks.sessions['sess-switch'];
|
|
454
|
-
const next = anchor.routeTask(tasks, {
|
|
455
|
-
sessionId: 'sess-switch',
|
|
456
|
-
batchKey: 'rb_strong_continue',
|
|
457
|
-
messageIds: ['m2'],
|
|
458
|
-
query: '把刚才那个再调一下',
|
|
459
|
-
registeredSeries: ['口播'],
|
|
460
|
-
now: 2000,
|
|
461
|
-
});
|
|
462
|
-
assert.notEqual(next.decision.kind, 'unbound');
|
|
463
|
-
assert.equal(next.anchor.episodeId, previous.episodeId);
|
|
464
|
-
assert.equal(next.anchor.seriesId, '口播');
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
test('R29 带「继续改」但对象换成新的英文主题时撤销旧范围', () => {
|
|
468
|
-
const tasks = activeVideoSession();
|
|
469
|
-
const next = anchor.routeTask(tasks, {
|
|
470
|
-
sessionId: 'sess-switch',
|
|
471
|
-
batchKey: 'rb_switch_pg_continue',
|
|
472
|
-
messageIds: ['m2'],
|
|
473
|
-
query: '继续改 PostgreSQL 连接池配置',
|
|
474
|
-
registeredSeries: ['口播'],
|
|
475
|
-
now: 2000,
|
|
476
|
-
});
|
|
477
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
478
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
479
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
test('R30 带「调整一下」但对象换了时撤销旧范围', () => {
|
|
483
|
-
const tasks = activeVideoSession();
|
|
484
|
-
const next = anchor.routeTask(tasks, {
|
|
485
|
-
sessionId: 'sess-switch',
|
|
486
|
-
batchKey: 'rb_switch_pg_adjust',
|
|
487
|
-
messageIds: ['m2'],
|
|
488
|
-
query: '调整一下 PostgreSQL 连接池配置',
|
|
489
|
-
registeredSeries: ['口播'],
|
|
490
|
-
now: 2000,
|
|
491
|
-
});
|
|
492
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
493
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
test('R31 带「继续改」但明确指向新对象动作时撤销旧范围', () => {
|
|
497
|
-
const tasks = activeVideoSession();
|
|
498
|
-
const next = anchor.routeTask(tasks, {
|
|
499
|
-
sessionId: 'sess-switch',
|
|
500
|
-
batchKey: 'rb_switch_db_code',
|
|
501
|
-
messageIds: ['m2'],
|
|
502
|
-
query: '继续改数据库连接超时的处理代码',
|
|
503
|
-
registeredSeries: ['口播'],
|
|
504
|
-
now: 2000,
|
|
505
|
-
});
|
|
506
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
507
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
/** 复核复现:句首带「继续」不能成为沿用旧正文的通行证。 */
|
|
511
|
-
for (const [label, query] of [
|
|
512
|
-
['R32 继续改数据库连接池配置', '继续改数据库连接池配置'],
|
|
513
|
-
['R33 继续优化数据库连接池', '继续优化数据库连接池'],
|
|
514
|
-
['R34 继续写数据库故障报告', '继续写数据库故障报告'],
|
|
515
|
-
['R35 这个数据库连接池配置怎么改', '这个数据库连接池配置怎么改'],
|
|
516
|
-
]) {
|
|
517
|
-
test(`${label}:与旧任务没有对象交集时不继承旧范围`, () => {
|
|
518
|
-
const tasks = activeVideoSession();
|
|
519
|
-
const next = anchor.routeTask(tasks, {
|
|
520
|
-
sessionId: 'sess-switch',
|
|
521
|
-
batchKey: `rb_${label.slice(0, 3)}_db`,
|
|
522
|
-
messageIds: ['m2'],
|
|
523
|
-
query,
|
|
524
|
-
registeredSeries: ['口播'],
|
|
525
|
-
now: 2000,
|
|
526
|
-
});
|
|
527
|
-
assert.equal(next.decision.kind, 'unbound');
|
|
528
|
-
assert.equal(next.decision.reason, 'new_task');
|
|
529
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
test('R41 旧稿提到但明确换到排查任务时不再绑定旧栏目', () => {
|
|
534
|
-
const tasks = activeVideoSession();
|
|
535
|
-
for (const query of ['排查 PostgreSQL 连接池故障,旧稿先放着', '这版数据库连接池配置怎么改']) {
|
|
536
|
-
const next = anchor.routeTask(tasks, {
|
|
537
|
-
sessionId: 'sess-switch',
|
|
538
|
-
batchKey: `rb_${query.length}`,
|
|
539
|
-
messageIds: ['m2'],
|
|
540
|
-
query,
|
|
541
|
-
registeredSeries: ['口播'],
|
|
542
|
-
now: 2000,
|
|
543
|
-
});
|
|
544
|
-
assert.equal(next.decision.kind, 'unbound', query);
|
|
545
|
-
assert.equal(next.anchor.seriesId, undefined, query);
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
test('R42 栏目只被提到、否定或搁置时都不算用户选中', () => {
|
|
550
|
-
assert.equal(anchor.seriesChosenBy('把口播开头改一下', '口播'), true);
|
|
551
|
-
assert.equal(anchor.seriesChosenBy('先不做口播了', '口播'), false);
|
|
552
|
-
assert.equal(anchor.seriesChosenBy('不要按口播的规矩来', '口播'), false);
|
|
553
|
-
assert.equal(anchor.seriesChosenBy('日志里提到口播吗', '口播'), false);
|
|
554
|
-
assert.equal(anchor.seriesChosenBy('先放着口播,改数据库', '口播'), false);
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
test('R43 任务类型判断:工程词优先于创作词,纯创作才归 content', () => {
|
|
558
|
-
assert.equal(anchor.taskKindOf('审阅口播插件的源码,修复上下文串扰问题'), 'code');
|
|
559
|
-
assert.equal(anchor.taskKindOf('日志里提到口播'), 'code');
|
|
560
|
-
assert.equal(anchor.taskKindOf('先不做口播,排查 PostgreSQL 连接池故障'), 'code');
|
|
561
|
-
assert.equal(anchor.taskKindOf('再做一期口播'), 'content');
|
|
562
|
-
assert.equal(anchor.taskKindOf('改成口播'), 'content');
|
|
563
|
-
assert.equal(anchor.taskKindOf('帮我看看这个'), '');
|
|
564
|
-
assert.equal(anchor.taskKindOf(''), '');
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
test('R44 栏目名后跟代码对象时只算读源码,不算选中栏目', () => {
|
|
568
|
-
assert.equal(anchor.seriesChosenBy('审阅口播插件的源码,修复上下文串扰问题', '口播'), false);
|
|
569
|
-
assert.equal(anchor.seriesChosenBy('检查口播插件代码', '口播'), false);
|
|
570
|
-
assert.equal(anchor.seriesChosenBy('口播源码里日志有问题', '口播'), false);
|
|
571
|
-
assert.equal(anchor.seriesChosenBy('再做一期口播', '口播'), true);
|
|
572
|
-
assert.equal(anchor.seriesChosenBy('改成口播', '口播'), true);
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
test('R45 明确撤销栏目:先不做口播优先于续作和旧栏目', () => {
|
|
576
|
-
const tasks = activeVideoSession();
|
|
577
|
-
const next = anchor.routeTask(tasks, {
|
|
578
|
-
sessionId: 'sess-switch',
|
|
579
|
-
batchKey: 'rb_revoke',
|
|
580
|
-
messageIds: ['m2'],
|
|
581
|
-
query: '先不做口播,排查 PostgreSQL 连接池故障',
|
|
582
|
-
registeredSeries: ['口播'],
|
|
583
|
-
now: 2000,
|
|
584
|
-
});
|
|
585
|
-
assert.equal(next.decision.kind, 'revoked');
|
|
586
|
-
assert.deepEqual(next.decision.revoked, ['口播']);
|
|
587
|
-
assert.equal(next.anchor.status, 'active');
|
|
588
|
-
assert.equal(next.anchor.seriesId, undefined);
|
|
589
|
-
assert.equal(next.anchor.taskKind, 'code');
|
|
590
|
-
assert.equal(next.episode.taskKind, 'code');
|
|
591
|
-
assert.equal(next.episode.previousEpisodeId, tasks.sessions['sess-switch'].episodeId);
|
|
592
|
-
assert.equal(anchor.revokeSeriesBy('不要按口播的规矩来', ['口播']).length, 0);
|
|
593
|
-
assert.deepEqual(anchor.revokeSeriesBy('先不做口播,排查故障', ['口播', 'Vlog']), ['口播']);
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
test('R46 读源码/提到栏目都不建内容绑定,项目默认值不能趁机补栏目', () => {
|
|
597
|
-
const tasks = anchor.emptyTasks();
|
|
598
|
-
tasks.defaults = { taskKind: 'video', seriesId: '口播' };
|
|
599
|
-
const reading = anchor.routeTask(tasks, {
|
|
600
|
-
sessionId: 'sess-reading', batchKey: 'rb_reading', messageIds: ['m1'],
|
|
601
|
-
query: '审阅口播插件的源码,修复上下文串扰问题', registeredSeries: ['口播'], now: 1000,
|
|
602
|
-
});
|
|
603
|
-
assert.equal(reading.anchor.seriesId, undefined);
|
|
604
|
-
assert.equal(reading.episode.taskKind, 'code');
|
|
605
|
-
assert.notEqual(reading.decision.kind, 'selected');
|
|
606
|
-
assert.notEqual(reading.decision.kind, 'default');
|
|
607
|
-
|
|
608
|
-
const mention = anchor.routeTask(tasks, {
|
|
609
|
-
sessionId: 'sess-mention', batchKey: 'rb_mention', messageIds: ['m1'],
|
|
610
|
-
query: '日志里提到口播', registeredSeries: ['口播'], now: 1000,
|
|
611
|
-
});
|
|
612
|
-
assert.equal(mention.anchor.seriesId, undefined);
|
|
613
|
-
assert.equal(mention.anchor.taskKind, 'code');
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
test('R47 项目默认值只补缺失字段,不覆盖显式任务类型和撤销', () => {
|
|
617
|
-
const tasks = anchor.emptyTasks();
|
|
618
|
-
tasks.defaults = { taskKind: 'video', seriesId: '口播' };
|
|
619
|
-
const filled = anchor.routeTask(tasks, {
|
|
620
|
-
sessionId: 'sess-default', batchKey: 'rb_default', messageIds: ['m1'],
|
|
621
|
-
query: '继续改上次那期的开头', registeredSeries: ['口播'], now: 1000,
|
|
622
|
-
});
|
|
623
|
-
assert.equal(filled.decision.kind, 'default');
|
|
624
|
-
assert.equal(filled.anchor.seriesId, '口播');
|
|
625
|
-
assert.equal(filled.anchor.taskKind, 'video');
|
|
626
|
-
|
|
627
|
-
const explicit = anchor.routeTask(tasks, {
|
|
628
|
-
sessionId: 'sess-explicit-kind', batchKey: 'rb_explicit_kind', messageIds: ['m1'],
|
|
629
|
-
query: '帮我看看 PostgreSQL 连接池', taskKind: 'code', registeredSeries: ['口播'], now: 1000,
|
|
630
|
-
});
|
|
631
|
-
assert.equal(explicit.anchor.taskKind, 'code');
|
|
632
|
-
assert.equal(explicit.anchor.seriesId, undefined);
|
|
633
|
-
assert.equal(explicit.decision.taskKind, 'code');
|
|
634
|
-
assert.notEqual(explicit.decision.kind, 'default');
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
test('R48 RuntimeContext 形状确定,任务字段来自锚点与本句判断', () => {
|
|
638
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), {
|
|
639
|
-
sessionId: 'sess-ctx', batchKey: 'rb_ctx', messageIds: ['m1'],
|
|
640
|
-
query: '审阅口播插件的源码,修复上下文串扰问题', registeredSeries: ['口播'], now: 1000,
|
|
641
|
-
});
|
|
642
|
-
const ctx = anchor.buildRuntimeContext({
|
|
643
|
-
projectId: '/work/demo', sessionId: 'sess-ctx', batchKey: 'rb_ctx', messageIds: ['m1'],
|
|
644
|
-
controlEpoch: 7, query: '继续看这段代码', anchor: routed.anchor, episode: routed.episode,
|
|
645
|
-
});
|
|
646
|
-
assert.deepEqual(Object.keys(ctx).sort(), ['controlEpoch', 'projectId', 'requestId', 'sessionId', 'task']);
|
|
647
|
-
assert.deepEqual(Object.keys(ctx.task).sort(), ['action', 'actionEvidence', 'artifactKey', 'conflict', 'evidence', 'kind', 'phase', 'seriesEvidence', 'seriesId', 'topic', 'topicEvidence']);
|
|
648
|
-
assert.equal(ctx.projectId, '/work/demo');
|
|
649
|
-
assert.equal(ctx.sessionId, 'sess-ctx');
|
|
650
|
-
assert.equal(ctx.requestId, anchor.contextIdFor('/work/demo', 'rb_ctx', routed.episode.episodeId));
|
|
651
|
-
assert.equal(ctx.controlEpoch, '7');
|
|
652
|
-
assert.equal(ctx.task.kind, 'code');
|
|
653
|
-
assert.equal(ctx.task.seriesId, '');
|
|
654
|
-
assert.equal(ctx.task.phase, 'brief');
|
|
655
|
-
assert.equal(ctx.task.evidence, 'query');
|
|
656
|
-
|
|
657
|
-
const inferred = anchor.buildRuntimeContext({
|
|
658
|
-
projectId: '/work/demo', sessionId: 'sess-ctx', batchKey: 'rb_ctx', messageIds: ['m1'],
|
|
659
|
-
query: '审阅口播插件的源码',
|
|
660
|
-
});
|
|
661
|
-
assert.equal(inferred.task.kind, 'code');
|
|
662
|
-
assert.equal(inferred.task.evidence, 'query');
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
test('R49 动作/交付物/话题分开:代码 bug 不把「口播」绑成栏目', () => {
|
|
666
|
-
const query = '口播功能最近有 bug,请修复代码';
|
|
667
|
-
const facets = anchor.taskFacetsOf(query);
|
|
668
|
-
assert.equal(facets.action.kind, 'fix');
|
|
669
|
-
assert.equal(facets.deliverable.kind, 'code');
|
|
670
|
-
assert.equal(facets.topic.text, '口播功能');
|
|
671
|
-
assert.equal(facets.topic.evidence, 'inferred');
|
|
672
|
-
assert.equal(anchor.seriesChosenBy(query, '口播'), false);
|
|
673
|
-
assert.deepEqual(anchor.revokeSeriesBy(query, ['口播']), []);
|
|
674
|
-
|
|
675
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), {
|
|
676
|
-
sessionId: 'sess-boundary-fix', batchKey: 'rb_boundary_fix', messageIds: ['m1'],
|
|
677
|
-
query, registeredSeries: ['口播'], now: 1000,
|
|
678
|
-
});
|
|
679
|
-
assert.notEqual(routed.decision.kind, 'selected');
|
|
680
|
-
assert.equal(routed.episode.taskKind, 'code');
|
|
681
|
-
assert.equal(routed.episode.kindEvidence, 'query');
|
|
682
|
-
assert.equal(routed.anchor.seriesId, undefined);
|
|
683
|
-
assert.equal(routed.episode.actionKind, 'fix');
|
|
684
|
-
|
|
685
|
-
const ctx = anchor.buildRuntimeContext({
|
|
686
|
-
projectId: '/work/demo', sessionId: 'sess-boundary-fix', batchKey: 'rb_boundary_fix',
|
|
687
|
-
messageIds: ['m1'], query: '继续修', anchor: routed.anchor, episode: routed.episode,
|
|
688
|
-
});
|
|
689
|
-
assert.equal(ctx.task.kind, 'code');
|
|
690
|
-
assert.equal(ctx.task.evidence, 'query');
|
|
691
|
-
assert.equal(ctx.task.seriesId, '');
|
|
692
|
-
assert.equal(ctx.task.action, 'fix');
|
|
693
|
-
});
|
|
694
|
-
|
|
695
|
-
test('R50 内容任务:数据库事务只是话题,口播才是交付栏目', () => {
|
|
696
|
-
const query = '做一期口播,讲解数据库事务,给普通观众听';
|
|
697
|
-
const facets = anchor.taskFacetsOf(query);
|
|
698
|
-
assert.equal(facets.action.kind, 'create');
|
|
699
|
-
assert.equal(facets.deliverable.kind, 'content');
|
|
700
|
-
assert.equal(facets.topic.text, '数据库事务');
|
|
701
|
-
assert.equal(facets.topic.evidence, 'query');
|
|
702
|
-
assert.equal(facets.conflict, false);
|
|
703
|
-
assert.equal(anchor.taskKindOf(query), 'content');
|
|
704
|
-
assert.equal(anchor.seriesChosenBy(query, '口播'), true);
|
|
705
|
-
assert.equal(anchor.seriesChosenBy(query, '数据库'), false);
|
|
706
|
-
assert.equal(anchor.seriesChosenBy(query, '数据库事务'), false);
|
|
707
|
-
|
|
708
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), {
|
|
709
|
-
sessionId: 'sess-boundary-content', batchKey: 'rb_boundary_content', messageIds: ['m1'],
|
|
710
|
-
query, registeredSeries: ['口播', '数据库', '数据库事务'], now: 1000,
|
|
711
|
-
});
|
|
712
|
-
assert.equal(routed.decision.kind, 'selected');
|
|
713
|
-
assert.equal(routed.anchor.seriesId, '口播');
|
|
714
|
-
assert.equal(routed.anchor.taskKind, 'content');
|
|
715
|
-
assert.equal(routed.episode.kindEvidence, 'query');
|
|
716
|
-
assert.equal(routed.episode.seriesEvidence, 'query');
|
|
717
|
-
assert.equal(routed.episode.topicText, '数据库事务');
|
|
718
|
-
assert.equal(routed.episode.topicEvidence, 'query');
|
|
719
|
-
|
|
720
|
-
/** 落过一次盘之后,query 推断仍不能自升成用户确认。 */
|
|
721
|
-
const ctx = anchor.buildRuntimeContext({
|
|
722
|
-
projectId: '/work/demo', sessionId: 'sess-boundary-content', batchKey: 'rb_boundary_content',
|
|
723
|
-
messageIds: ['m1'], query: '继续', anchor: routed.anchor, episode: routed.episode,
|
|
724
|
-
});
|
|
725
|
-
assert.equal(ctx.task.kind, 'content');
|
|
726
|
-
assert.equal(ctx.task.evidence, 'query');
|
|
727
|
-
assert.equal(ctx.task.seriesEvidence, 'query');
|
|
728
|
-
assert.equal(ctx.task.topic, '数据库事务');
|
|
729
|
-
assert.equal(ctx.task.topicEvidence, 'query');
|
|
730
|
-
});
|
|
731
|
-
|
|
732
|
-
test('R51 句末语气词不是撤销;只有明确的停止动作才撤销栏目', () => {
|
|
733
|
-
const query = '做一期口播吧';
|
|
734
|
-
assert.deepEqual(anchor.revokeSeriesBy(query, ['口播']), []);
|
|
735
|
-
assert.equal(anchor.seriesChosenBy(query, '口播'), true);
|
|
736
|
-
assert.equal(anchor.taskKindOf(query), 'content');
|
|
737
|
-
|
|
738
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), {
|
|
739
|
-
sessionId: 'sess-boundary-particle', batchKey: 'rb_boundary_particle', messageIds: ['m1'],
|
|
740
|
-
query, registeredSeries: ['口播'], now: 1000,
|
|
741
|
-
});
|
|
742
|
-
assert.equal(routed.decision.kind, 'selected');
|
|
743
|
-
assert.equal(routed.anchor.seriesId, '口播');
|
|
744
|
-
assert.equal(routed.anchor.taskKind, 'content');
|
|
745
|
-
assert.equal(routed.episode.kindEvidence, 'query');
|
|
746
|
-
|
|
747
|
-
for (const soft of ['做一期口播吧', '做一期口播啦', '做一期口播了', '做一期口播吧!']) {
|
|
748
|
-
assert.deepEqual(anchor.revokeSeriesBy(soft, ['口播']), [], soft);
|
|
749
|
-
}
|
|
750
|
-
for (const hard of ['先不做口播了', '口播不做了', '暂停口播', '放一边口播', '口播先不做']) {
|
|
751
|
-
assert.deepEqual(anchor.revokeSeriesBy(hard, ['口播']), ['口播'], hard);
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
test('R52 中英文标点与同义词:代码对象、话题前缀、内容用法都按同一规则', () => {
|
|
756
|
-
for (const query of [
|
|
757
|
-
'口播功能最近有 bug, 请修复代码.',
|
|
758
|
-
'口播模块报错,帮我排查',
|
|
759
|
-
'口播页面有个故障!',
|
|
760
|
-
'口播组件接口流程逻辑都改一下',
|
|
761
|
-
]) {
|
|
762
|
-
assert.equal(anchor.seriesChosenBy(query, '口播'), false, query);
|
|
763
|
-
assert.equal(anchor.taskKindOf(query), 'code', query);
|
|
764
|
-
}
|
|
765
|
-
for (const soft of ['做一期口播吧!', '做一期口播啦。', '做一期口播了?']) {
|
|
766
|
-
assert.equal(anchor.seriesChosenBy(soft, '口播'), true, soft);
|
|
767
|
-
assert.equal(anchor.taskKindOf(soft), 'content', soft);
|
|
768
|
-
}
|
|
769
|
-
/** 「做一期口播功能」是内容用法,不能因为「功能」就否掉栏目。 */
|
|
770
|
-
assert.equal(anchor.seriesChosenBy('做一期口播功能', '口播'), true);
|
|
771
|
-
assert.equal(anchor.taskKindOf('做一期口播功能'), 'content');
|
|
772
|
-
|
|
773
|
-
for (const query of ['做一期口播, 讲解数据库事务, 给普通观众听.', '做一期口播;介绍数据库事务;给普通观众听']) {
|
|
774
|
-
assert.equal(anchor.taskKindOf(query), 'content', query);
|
|
775
|
-
assert.equal(anchor.seriesChosenBy(query, '口播'), true, query);
|
|
776
|
-
assert.equal(anchor.seriesChosenBy(query, '数据库'), false, query);
|
|
777
|
-
}
|
|
778
|
-
for (const query of ['聊聊数据库事务', '主题是数据库事务', '围绕数据库事务讲讲']) {
|
|
779
|
-
assert.equal(anchor.seriesChosenBy(query, '数据库'), false, query);
|
|
780
|
-
assert.equal(anchor.seriesChosenBy(query, '数据库事务'), false, query);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
const conflict = anchor.taskFacetsOf('日志里提到口播,顺便讲数据库事务');
|
|
784
|
-
assert.equal(conflict.conflict, true);
|
|
785
|
-
const routed = anchor.routeTask(anchor.emptyTasks(), {
|
|
786
|
-
sessionId: 'sess-boundary-conflict', batchKey: 'rb_boundary_conflict', messageIds: ['m1'],
|
|
787
|
-
query: '日志里提到口播,顺便讲数据库事务', registeredSeries: ['口播'], now: 1000,
|
|
788
|
-
});
|
|
789
|
-
assert.notEqual(routed.anchor.seriesId, '口播');
|
|
790
|
-
const ctx = anchor.buildRuntimeContext({
|
|
791
|
-
projectId: '/work/demo', sessionId: 'sess-boundary-conflict', batchKey: 'rb_boundary_conflict',
|
|
792
|
-
messageIds: ['m1'], query: '日志里提到口播,顺便讲数据库事务', anchor: routed.anchor, episode: routed.episode,
|
|
793
|
-
});
|
|
794
|
-
assert.equal(ctx.task.conflict, true);
|
|
795
|
-
});
|