@remnic/core 9.3.596 → 9.3.598
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/dist/access-cli.js +17 -17
- package/dist/access-http.js +6 -6
- package/dist/access-mcp.js +5 -5
- package/dist/access-service.js +4 -4
- package/dist/behavior-learner.js +2 -1
- package/dist/behavior-learner.js.map +1 -1
- package/dist/causal-behavior.js +3 -3
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.js +4 -4
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory.js +2 -2
- package/dist/{chunk-A2Z6UCWT.js → chunk-33JBK2XP.js} +2 -2
- package/dist/{chunk-D2MMMTDV.js → chunk-5SQ5CQJP.js} +2 -2
- package/dist/{chunk-F4LM4ULA.js → chunk-65JSA4MP.js} +12 -12
- package/dist/{chunk-SKGV326D.js → chunk-6GDHLVJC.js} +2 -2
- package/dist/chunk-6HEM6HTQ.js +359 -0
- package/dist/chunk-6HEM6HTQ.js.map +1 -0
- package/dist/{chunk-WXACKLKP.js → chunk-75O6YQ63.js} +22 -7
- package/dist/chunk-75O6YQ63.js.map +1 -0
- package/dist/{chunk-D65TSG24.js → chunk-7DZRO2DC.js} +2 -2
- package/dist/{chunk-TYICDVQW.js → chunk-BDCCWRHR.js} +4 -4
- package/dist/{chunk-LYPDMKUT.js → chunk-CL3MWNNQ.js} +2 -2
- package/dist/{chunk-YQMZ7IH2.js → chunk-D4KJ74JJ.js} +65 -27
- package/dist/chunk-D4KJ74JJ.js.map +1 -0
- package/dist/{chunk-W5O2FQTZ.js → chunk-GUPISBV2.js} +2 -2
- package/dist/{chunk-472U7RDF.js → chunk-JGSKJHF7.js} +2 -2
- package/dist/{chunk-IEFHBIU2.js → chunk-KDUVQU6Y.js} +14 -14
- package/dist/{chunk-6HZ6AO2P.js → chunk-LBJBNWS2.js} +37 -10
- package/dist/chunk-LBJBNWS2.js.map +1 -0
- package/dist/{chunk-Z4R6RI2N.js → chunk-NSKYFGDL.js} +2 -2
- package/dist/{chunk-OD5LFAPZ.js → chunk-V67GWXM2.js} +1 -1
- package/dist/{chunk-5NXIJZFX.js → chunk-WR64DQFE.js} +3 -3
- package/dist/{chunk-5BUGGPBR.js → chunk-WZA5Y6AC.js} +3 -3
- package/dist/chunk-ZBJMUXZH.js +121 -0
- package/dist/chunk-ZBJMUXZH.js.map +1 -0
- package/dist/{chunk-XPXEJRUB.js → chunk-ZRWB5D4H.js} +2 -2
- package/dist/{chunk-MA5MWGKP.js → chunk-ZT3EGNLR.js} +2 -2
- package/dist/{chunk-LMPHTYJC.js → chunk-ZZYF3BUL.js} +2 -2
- package/dist/cli.js +14 -14
- package/dist/compounding/engine.js +1 -1
- package/dist/direct-answer-wiring.js +3 -3
- package/dist/direct-answer.d.ts +1 -1
- package/dist/direct-answer.js +2 -2
- package/dist/harmonic-retrieval.js +2 -2
- package/dist/index.js +21 -21
- package/dist/orchestrator.js +16 -16
- package/dist/policy-runtime.js +3 -2
- package/dist/recall-query-policy.js +2 -1
- package/dist/recall-tokenization.d.ts +5 -1
- package/dist/recall-tokenization.js +3 -1
- package/dist/resume-bundles.js +3 -3
- package/dist/retrieval-agents.js +2 -2
- package/dist/semantic-consolidation.js +2 -2
- package/dist/semantic-rule-verifier.js +2 -2
- package/dist/temporal-index.js +1 -1
- package/dist/trust-zones.js +2 -2
- package/dist/verified-recall.js +2 -2
- package/dist/work-product-ledger.js +2 -2
- package/package.json +1 -1
- package/src/causal-chain.ts +80 -42
- package/src/direct-answer.test.ts +618 -15
- package/src/direct-answer.ts +259 -20
- package/src/recall-query-policy.ts +49 -27
- package/src/recall-tokenization.ts +131 -21
- package/src/temporal-index.ts +23 -6
- package/dist/chunk-6HZ6AO2P.js.map +0 -1
- package/dist/chunk-DT5TVLJE.js +0 -32
- package/dist/chunk-DT5TVLJE.js.map +0 -1
- package/dist/chunk-WXACKLKP.js.map +0 -1
- package/dist/chunk-Y4FHOFJ2.js +0 -140
- package/dist/chunk-Y4FHOFJ2.js.map +0 -1
- package/dist/chunk-YQMZ7IH2.js.map +0 -1
- /package/dist/{chunk-A2Z6UCWT.js.map → chunk-33JBK2XP.js.map} +0 -0
- /package/dist/{chunk-D2MMMTDV.js.map → chunk-5SQ5CQJP.js.map} +0 -0
- /package/dist/{chunk-F4LM4ULA.js.map → chunk-65JSA4MP.js.map} +0 -0
- /package/dist/{chunk-SKGV326D.js.map → chunk-6GDHLVJC.js.map} +0 -0
- /package/dist/{chunk-D65TSG24.js.map → chunk-7DZRO2DC.js.map} +0 -0
- /package/dist/{chunk-TYICDVQW.js.map → chunk-BDCCWRHR.js.map} +0 -0
- /package/dist/{chunk-LYPDMKUT.js.map → chunk-CL3MWNNQ.js.map} +0 -0
- /package/dist/{chunk-W5O2FQTZ.js.map → chunk-GUPISBV2.js.map} +0 -0
- /package/dist/{chunk-472U7RDF.js.map → chunk-JGSKJHF7.js.map} +0 -0
- /package/dist/{chunk-IEFHBIU2.js.map → chunk-KDUVQU6Y.js.map} +0 -0
- /package/dist/{chunk-Z4R6RI2N.js.map → chunk-NSKYFGDL.js.map} +0 -0
- /package/dist/{chunk-OD5LFAPZ.js.map → chunk-V67GWXM2.js.map} +0 -0
- /package/dist/{chunk-5NXIJZFX.js.map → chunk-WR64DQFE.js.map} +0 -0
- /package/dist/{chunk-5BUGGPBR.js.map → chunk-WZA5Y6AC.js.map} +0 -0
- /package/dist/{chunk-XPXEJRUB.js.map → chunk-ZRWB5D4H.js.map} +0 -0
- /package/dist/{chunk-MA5MWGKP.js.map → chunk-ZT3EGNLR.js.map} +0 -0
- /package/dist/{chunk-LMPHTYJC.js.map → chunk-ZZYF3BUL.js.map} +0 -0
|
@@ -2,10 +2,10 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
FILTER_LABELS,
|
|
6
|
-
isDirectAnswerEligible,
|
|
7
5
|
type DirectAnswerCandidate,
|
|
8
6
|
type DirectAnswerConfig,
|
|
7
|
+
FILTER_LABELS,
|
|
8
|
+
isDirectAnswerEligible,
|
|
9
9
|
} from "./direct-answer.js";
|
|
10
10
|
import type { MemoryFile } from "./types.js";
|
|
11
11
|
|
|
@@ -17,15 +17,17 @@ const DEFAULT_CONFIG: DirectAnswerConfig = {
|
|
|
17
17
|
eligibleTaxonomyBuckets: ["decisions", "principles", "conventions", "runbooks", "entities"],
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
function makeMemory(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
function makeMemory(
|
|
21
|
+
overrides: {
|
|
22
|
+
id?: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
content?: string;
|
|
25
|
+
tags?: string[];
|
|
26
|
+
status?: MemoryFile["frontmatter"]["status"];
|
|
27
|
+
verificationState?: MemoryFile["frontmatter"]["verificationState"];
|
|
28
|
+
entityRef?: string;
|
|
29
|
+
} = {}
|
|
30
|
+
): MemoryFile {
|
|
29
31
|
const id = overrides.id ?? "mem-1";
|
|
30
32
|
return {
|
|
31
33
|
path: overrides.path ?? `/memory/${id}.md`,
|
|
@@ -46,14 +48,15 @@ function makeMemory(overrides: {
|
|
|
46
48
|
};
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
function makeCandidate(
|
|
51
|
+
function makeCandidate(
|
|
52
|
+
overrides: Partial<DirectAnswerCandidate> & { memory?: MemoryFile } = {}
|
|
53
|
+
): DirectAnswerCandidate {
|
|
50
54
|
return {
|
|
51
55
|
memory: overrides.memory ?? makeMemory(),
|
|
52
56
|
// Use `in` so callers can pass an explicit `null` without it being
|
|
53
57
|
// clobbered by the default.
|
|
54
|
-
trustZone: "trustZone" in overrides ? overrides.trustZone ?? null : "trusted",
|
|
55
|
-
taxonomyBucket:
|
|
56
|
-
"taxonomyBucket" in overrides ? overrides.taxonomyBucket ?? null : "decisions",
|
|
58
|
+
trustZone: "trustZone" in overrides ? (overrides.trustZone ?? null) : "trusted",
|
|
59
|
+
taxonomyBucket: "taxonomyBucket" in overrides ? (overrides.taxonomyBucket ?? null) : "decisions",
|
|
57
60
|
importanceScore: overrides.importanceScore ?? 0.9,
|
|
58
61
|
matchScore: overrides.matchScore,
|
|
59
62
|
};
|
|
@@ -350,6 +353,555 @@ test("isDirectAnswerEligible rejects when no candidate meets the token-overlap f
|
|
|
350
353
|
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
351
354
|
});
|
|
352
355
|
|
|
356
|
+
test("isDirectAnswerEligible does not pass CJK direct answers on shared common characters", () => {
|
|
357
|
+
const candidate = makeCandidate({
|
|
358
|
+
memory: makeMemory({
|
|
359
|
+
tags: [],
|
|
360
|
+
content: "用户讨厌深色主题",
|
|
361
|
+
}),
|
|
362
|
+
});
|
|
363
|
+
const result = isDirectAnswerEligible({
|
|
364
|
+
query: "用户喜欢深色模式",
|
|
365
|
+
candidates: [candidate],
|
|
366
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
367
|
+
});
|
|
368
|
+
assert.equal(result.eligible, false);
|
|
369
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
370
|
+
assert.ok(result.tokenOverlap === undefined);
|
|
371
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("isDirectAnswerEligible does not pass short CJK direct answers on shared suffixes", () => {
|
|
375
|
+
const candidate = makeCandidate({
|
|
376
|
+
memory: makeMemory({
|
|
377
|
+
tags: [],
|
|
378
|
+
content: "用户喜欢浅色模式",
|
|
379
|
+
}),
|
|
380
|
+
});
|
|
381
|
+
const result = isDirectAnswerEligible({
|
|
382
|
+
query: "深色模式",
|
|
383
|
+
candidates: [candidate],
|
|
384
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
385
|
+
});
|
|
386
|
+
assert.equal(result.eligible, false);
|
|
387
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
388
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test("isDirectAnswerEligible does not pass CJK direct answers on shared prefixes with opposite values", () => {
|
|
392
|
+
const candidate = makeCandidate({
|
|
393
|
+
memory: makeMemory({
|
|
394
|
+
tags: [],
|
|
395
|
+
content: "用户喜欢浅色模式",
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
const result = isDirectAnswerEligible({
|
|
399
|
+
query: "用户喜欢深色模式",
|
|
400
|
+
candidates: [candidate],
|
|
401
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
402
|
+
});
|
|
403
|
+
assert.equal(result.eligible, false);
|
|
404
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
405
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test("isDirectAnswerEligible requires every independent CJK phrase before direct answering", () => {
|
|
409
|
+
const candidate = makeCandidate({
|
|
410
|
+
memory: makeMemory({
|
|
411
|
+
tags: [],
|
|
412
|
+
content: "用户喜欢深色模式",
|
|
413
|
+
}),
|
|
414
|
+
});
|
|
415
|
+
const result = isDirectAnswerEligible({
|
|
416
|
+
query: "用户喜欢深色模式 and 客户账户升级",
|
|
417
|
+
candidates: [candidate],
|
|
418
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
419
|
+
});
|
|
420
|
+
assert.equal(result.eligible, false);
|
|
421
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
422
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test("isDirectAnswerEligible does not require punctuation-bridged CJK aggregate phrases", () => {
|
|
426
|
+
const candidate = makeCandidate({
|
|
427
|
+
memory: makeMemory({
|
|
428
|
+
tags: [],
|
|
429
|
+
content: "用户喜欢深色模式 and 客户账户升级",
|
|
430
|
+
}),
|
|
431
|
+
});
|
|
432
|
+
const result = isDirectAnswerEligible({
|
|
433
|
+
query: "用户喜欢深色模式,客户账户升级",
|
|
434
|
+
candidates: [candidate],
|
|
435
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
436
|
+
});
|
|
437
|
+
assert.equal(result.eligible, true);
|
|
438
|
+
assert.equal(result.reason, "eligible");
|
|
439
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("isDirectAnswerEligible treats space-separated long CJK clauses as independent phrases", () => {
|
|
443
|
+
const candidate = makeCandidate({
|
|
444
|
+
memory: makeMemory({
|
|
445
|
+
tags: [],
|
|
446
|
+
content: "用户喜欢深色模式 and 客户账户升级",
|
|
447
|
+
}),
|
|
448
|
+
});
|
|
449
|
+
const result = isDirectAnswerEligible({
|
|
450
|
+
query: "用户喜欢深色模式 客户账户升级",
|
|
451
|
+
candidates: [candidate],
|
|
452
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
453
|
+
});
|
|
454
|
+
assert.equal(result.eligible, true);
|
|
455
|
+
assert.equal(result.reason, "eligible");
|
|
456
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("isDirectAnswerEligible bridges short spaced CJK query chunks", () => {
|
|
460
|
+
const candidate = makeCandidate({
|
|
461
|
+
memory: makeMemory({
|
|
462
|
+
tags: [],
|
|
463
|
+
content: "用户喜欢深色模式",
|
|
464
|
+
}),
|
|
465
|
+
});
|
|
466
|
+
const result = isDirectAnswerEligible({
|
|
467
|
+
query: "用户 喜欢 深色 模式",
|
|
468
|
+
candidates: [candidate],
|
|
469
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
470
|
+
});
|
|
471
|
+
assert.equal(result.eligible, true);
|
|
472
|
+
assert.equal(result.reason, "eligible");
|
|
473
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
test("isDirectAnswerEligible requires ASCII discriminators in mixed CJK terms", () => {
|
|
477
|
+
const candidate = makeCandidate({
|
|
478
|
+
memory: makeMemory({
|
|
479
|
+
tags: [],
|
|
480
|
+
content: "客户erp升级",
|
|
481
|
+
}),
|
|
482
|
+
});
|
|
483
|
+
const result = isDirectAnswerEligible({
|
|
484
|
+
query: "客户crm升级",
|
|
485
|
+
candidates: [candidate],
|
|
486
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
487
|
+
});
|
|
488
|
+
assert.equal(result.eligible, false);
|
|
489
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
490
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test("isDirectAnswerEligible accepts matching ASCII discriminators in mixed CJK terms", () => {
|
|
494
|
+
const candidate = makeCandidate({
|
|
495
|
+
memory: makeMemory({
|
|
496
|
+
tags: [],
|
|
497
|
+
content: "客户crm升级",
|
|
498
|
+
}),
|
|
499
|
+
});
|
|
500
|
+
const result = isDirectAnswerEligible({
|
|
501
|
+
query: "客户crm升级",
|
|
502
|
+
candidates: [candidate],
|
|
503
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
504
|
+
});
|
|
505
|
+
assert.equal(result.eligible, true);
|
|
506
|
+
assert.equal(result.reason, "eligible");
|
|
507
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test("isDirectAnswerEligible requires spaced ASCII discriminators in mixed CJK terms", () => {
|
|
511
|
+
const candidate = makeCandidate({
|
|
512
|
+
memory: makeMemory({
|
|
513
|
+
tags: [],
|
|
514
|
+
content: "客户 ERP 升级",
|
|
515
|
+
}),
|
|
516
|
+
});
|
|
517
|
+
const result = isDirectAnswerEligible({
|
|
518
|
+
query: "客户 CRM 升级",
|
|
519
|
+
candidates: [candidate],
|
|
520
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
521
|
+
});
|
|
522
|
+
assert.equal(result.eligible, false);
|
|
523
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
524
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test("isDirectAnswerEligible requires short spaced ASCII discriminators in mixed CJK terms", () => {
|
|
528
|
+
const candidate = makeCandidate({
|
|
529
|
+
memory: makeMemory({
|
|
530
|
+
tags: [],
|
|
531
|
+
content: "客户 v1 升级",
|
|
532
|
+
}),
|
|
533
|
+
});
|
|
534
|
+
const result = isDirectAnswerEligible({
|
|
535
|
+
query: "客户 v2 升级",
|
|
536
|
+
candidates: [candidate],
|
|
537
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
538
|
+
});
|
|
539
|
+
assert.equal(result.eligible, false);
|
|
540
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
541
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
test("isDirectAnswerEligible accepts matching spaced ASCII discriminators in mixed CJK terms", () => {
|
|
545
|
+
const candidate = makeCandidate({
|
|
546
|
+
memory: makeMemory({
|
|
547
|
+
tags: [],
|
|
548
|
+
content: "客户 CRM 升级",
|
|
549
|
+
}),
|
|
550
|
+
});
|
|
551
|
+
const result = isDirectAnswerEligible({
|
|
552
|
+
query: "客户 CRM 升级",
|
|
553
|
+
candidates: [candidate],
|
|
554
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
555
|
+
});
|
|
556
|
+
assert.equal(result.eligible, true);
|
|
557
|
+
assert.equal(result.reason, "eligible");
|
|
558
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
test("isDirectAnswerEligible accepts matching short spaced ASCII discriminators in mixed CJK terms", () => {
|
|
562
|
+
const candidate = makeCandidate({
|
|
563
|
+
memory: makeMemory({
|
|
564
|
+
tags: [],
|
|
565
|
+
content: "客户 v2 升级",
|
|
566
|
+
}),
|
|
567
|
+
});
|
|
568
|
+
const result = isDirectAnswerEligible({
|
|
569
|
+
query: "客户 v2 升级",
|
|
570
|
+
candidates: [candidate],
|
|
571
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
572
|
+
});
|
|
573
|
+
assert.equal(result.eligible, true);
|
|
574
|
+
assert.equal(result.reason, "eligible");
|
|
575
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
test("isDirectAnswerEligible requires end-boundary ASCII discriminators in mixed CJK terms", () => {
|
|
579
|
+
const candidate = makeCandidate({
|
|
580
|
+
memory: makeMemory({
|
|
581
|
+
tags: [],
|
|
582
|
+
content: "客户 ERP",
|
|
583
|
+
}),
|
|
584
|
+
});
|
|
585
|
+
const result = isDirectAnswerEligible({
|
|
586
|
+
query: "客户 CRM",
|
|
587
|
+
candidates: [candidate],
|
|
588
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
589
|
+
});
|
|
590
|
+
assert.equal(result.eligible, false);
|
|
591
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
592
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
test("isDirectAnswerEligible accepts matching end-boundary ASCII discriminators in mixed CJK terms", () => {
|
|
596
|
+
const candidate = makeCandidate({
|
|
597
|
+
memory: makeMemory({
|
|
598
|
+
tags: [],
|
|
599
|
+
content: "客户 CRM",
|
|
600
|
+
}),
|
|
601
|
+
});
|
|
602
|
+
const result = isDirectAnswerEligible({
|
|
603
|
+
query: "客户 CRM",
|
|
604
|
+
candidates: [candidate],
|
|
605
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
606
|
+
});
|
|
607
|
+
assert.equal(result.eligible, true);
|
|
608
|
+
assert.equal(result.reason, "eligible");
|
|
609
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("isDirectAnswerEligible requires start-boundary ASCII discriminators in mixed CJK terms", () => {
|
|
613
|
+
const candidate = makeCandidate({
|
|
614
|
+
memory: makeMemory({
|
|
615
|
+
tags: [],
|
|
616
|
+
content: "ERP 客户",
|
|
617
|
+
}),
|
|
618
|
+
});
|
|
619
|
+
const result = isDirectAnswerEligible({
|
|
620
|
+
query: "CRM 客户",
|
|
621
|
+
candidates: [candidate],
|
|
622
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
623
|
+
});
|
|
624
|
+
assert.equal(result.eligible, false);
|
|
625
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
626
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
test("isDirectAnswerEligible requires short CJK chunks around mixed-script discriminators", () => {
|
|
630
|
+
const candidate = makeCandidate({
|
|
631
|
+
memory: makeMemory({
|
|
632
|
+
tags: [],
|
|
633
|
+
content: "客户crm降级",
|
|
634
|
+
}),
|
|
635
|
+
});
|
|
636
|
+
const result = isDirectAnswerEligible({
|
|
637
|
+
query: "客户crm升级",
|
|
638
|
+
candidates: [candidate],
|
|
639
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
640
|
+
});
|
|
641
|
+
assert.equal(result.eligible, false);
|
|
642
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
643
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
test("isDirectAnswerEligible requires short contiguous ASCII discriminators in mixed CJK terms", () => {
|
|
647
|
+
const candidate = makeCandidate({
|
|
648
|
+
memory: makeMemory({
|
|
649
|
+
tags: [],
|
|
650
|
+
content: "客户v1升级",
|
|
651
|
+
}),
|
|
652
|
+
});
|
|
653
|
+
const result = isDirectAnswerEligible({
|
|
654
|
+
query: "客户v2升级",
|
|
655
|
+
candidates: [candidate],
|
|
656
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
657
|
+
});
|
|
658
|
+
assert.equal(result.eligible, false);
|
|
659
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
660
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
test("isDirectAnswerEligible accepts matching short contiguous ASCII discriminators in mixed CJK terms", () => {
|
|
664
|
+
const candidate = makeCandidate({
|
|
665
|
+
memory: makeMemory({
|
|
666
|
+
tags: [],
|
|
667
|
+
content: "客户v2升级",
|
|
668
|
+
}),
|
|
669
|
+
});
|
|
670
|
+
const result = isDirectAnswerEligible({
|
|
671
|
+
query: "客户v2升级",
|
|
672
|
+
candidates: [candidate],
|
|
673
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
674
|
+
});
|
|
675
|
+
assert.equal(result.eligible, true);
|
|
676
|
+
assert.equal(result.reason, "eligible");
|
|
677
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
test("isDirectAnswerEligible requires non-Latin discriminators in mixed CJK terms", () => {
|
|
681
|
+
const candidate = makeCandidate({
|
|
682
|
+
memory: makeMemory({
|
|
683
|
+
tags: [],
|
|
684
|
+
content: "客户α升级",
|
|
685
|
+
}),
|
|
686
|
+
});
|
|
687
|
+
const result = isDirectAnswerEligible({
|
|
688
|
+
query: "客户β升级",
|
|
689
|
+
candidates: [candidate],
|
|
690
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
691
|
+
});
|
|
692
|
+
assert.equal(result.eligible, false);
|
|
693
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
694
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
test("isDirectAnswerEligible accepts matching non-Latin discriminators in mixed CJK terms", () => {
|
|
698
|
+
const candidate = makeCandidate({
|
|
699
|
+
memory: makeMemory({
|
|
700
|
+
tags: [],
|
|
701
|
+
content: "客户β升级",
|
|
702
|
+
}),
|
|
703
|
+
});
|
|
704
|
+
const result = isDirectAnswerEligible({
|
|
705
|
+
query: "客户β升级",
|
|
706
|
+
candidates: [candidate],
|
|
707
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
708
|
+
});
|
|
709
|
+
assert.equal(result.eligible, true);
|
|
710
|
+
assert.equal(result.reason, "eligible");
|
|
711
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
test("isDirectAnswerEligible requires boundary non-Latin discriminators in mixed CJK terms", () => {
|
|
715
|
+
const candidate = makeCandidate({
|
|
716
|
+
memory: makeMemory({
|
|
717
|
+
tags: [],
|
|
718
|
+
content: "客户 α",
|
|
719
|
+
}),
|
|
720
|
+
});
|
|
721
|
+
const result = isDirectAnswerEligible({
|
|
722
|
+
query: "客户 β",
|
|
723
|
+
candidates: [candidate],
|
|
724
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
725
|
+
});
|
|
726
|
+
assert.equal(result.eligible, false);
|
|
727
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
728
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
test("isDirectAnswerEligible requires all non-CJK Unicode query tokens before direct answering", () => {
|
|
732
|
+
const candidate = makeCandidate({
|
|
733
|
+
memory: makeMemory({
|
|
734
|
+
tags: [],
|
|
735
|
+
content: "пользователь ненавидит темный режим",
|
|
736
|
+
}),
|
|
737
|
+
});
|
|
738
|
+
const result = isDirectAnswerEligible({
|
|
739
|
+
query: "пользователь любит темный режим",
|
|
740
|
+
candidates: [candidate],
|
|
741
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
742
|
+
});
|
|
743
|
+
assert.equal(result.eligible, false);
|
|
744
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
745
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
test("isDirectAnswerEligible accepts matching non-CJK Unicode query tokens", () => {
|
|
749
|
+
const candidate = makeCandidate({
|
|
750
|
+
memory: makeMemory({
|
|
751
|
+
tags: [],
|
|
752
|
+
content: "пользователь любит темный режим",
|
|
753
|
+
}),
|
|
754
|
+
});
|
|
755
|
+
const result = isDirectAnswerEligible({
|
|
756
|
+
query: "пользователь любит темный режим",
|
|
757
|
+
candidates: [candidate],
|
|
758
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
759
|
+
});
|
|
760
|
+
assert.equal(result.eligible, true);
|
|
761
|
+
assert.equal(result.reason, "eligible");
|
|
762
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
test("isDirectAnswerEligible requires ASCII discriminators in non-CJK Unicode queries", () => {
|
|
766
|
+
const candidate = makeCandidate({
|
|
767
|
+
memory: makeMemory({
|
|
768
|
+
tags: [],
|
|
769
|
+
content: "клиент erp режим",
|
|
770
|
+
}),
|
|
771
|
+
});
|
|
772
|
+
const result = isDirectAnswerEligible({
|
|
773
|
+
query: "клиент crm режим",
|
|
774
|
+
candidates: [candidate],
|
|
775
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
776
|
+
});
|
|
777
|
+
assert.equal(result.eligible, false);
|
|
778
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
779
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
test("isDirectAnswerEligible accepts matching ASCII discriminators in non-CJK Unicode queries", () => {
|
|
783
|
+
const candidate = makeCandidate({
|
|
784
|
+
memory: makeMemory({
|
|
785
|
+
tags: [],
|
|
786
|
+
content: "клиент crm режим",
|
|
787
|
+
}),
|
|
788
|
+
});
|
|
789
|
+
const result = isDirectAnswerEligible({
|
|
790
|
+
query: "клиент crm режим",
|
|
791
|
+
candidates: [candidate],
|
|
792
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
793
|
+
});
|
|
794
|
+
assert.equal(result.eligible, true);
|
|
795
|
+
assert.equal(result.reason, "eligible");
|
|
796
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test("isDirectAnswerEligible does not require English prompt words before non-CJK Unicode terms", () => {
|
|
800
|
+
const candidate = makeCandidate({
|
|
801
|
+
memory: makeMemory({
|
|
802
|
+
tags: [],
|
|
803
|
+
content: "клиент режим",
|
|
804
|
+
}),
|
|
805
|
+
});
|
|
806
|
+
for (const promptWord of ["find", "get", "status", "include"]) {
|
|
807
|
+
const result = isDirectAnswerEligible({
|
|
808
|
+
query: `${promptWord} клиент режим`,
|
|
809
|
+
candidates: [candidate],
|
|
810
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
811
|
+
});
|
|
812
|
+
assert.equal(result.eligible, true, promptWord);
|
|
813
|
+
assert.equal(result.reason, "eligible", promptWord);
|
|
814
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55, promptWord);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
test("isDirectAnswerEligible does not require Russian prompt words before non-CJK Unicode terms", () => {
|
|
819
|
+
const candidate = makeCandidate({
|
|
820
|
+
memory: makeMemory({
|
|
821
|
+
tags: [],
|
|
822
|
+
content: "пользователь любит темный режим",
|
|
823
|
+
}),
|
|
824
|
+
});
|
|
825
|
+
const result = isDirectAnswerEligible({
|
|
826
|
+
query: "найди пользователь любит темный режим",
|
|
827
|
+
candidates: [candidate],
|
|
828
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
829
|
+
});
|
|
830
|
+
assert.equal(result.eligible, true);
|
|
831
|
+
assert.equal(result.reason, "eligible");
|
|
832
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test("isDirectAnswerEligible matches CJK direct answers across inserted spaces", () => {
|
|
836
|
+
const candidate = makeCandidate({
|
|
837
|
+
memory: makeMemory({
|
|
838
|
+
tags: [],
|
|
839
|
+
content: "用户 喜欢 深色 模式",
|
|
840
|
+
}),
|
|
841
|
+
});
|
|
842
|
+
const result = isDirectAnswerEligible({
|
|
843
|
+
query: "用户喜欢深色模式",
|
|
844
|
+
candidates: [candidate],
|
|
845
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.55 },
|
|
846
|
+
});
|
|
847
|
+
assert.equal(result.eligible, true);
|
|
848
|
+
assert.equal(result.reason, "eligible");
|
|
849
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.55);
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
test("isDirectAnswerEligible does not require English prompt words before CJK terms", () => {
|
|
853
|
+
const candidate = makeCandidate({
|
|
854
|
+
memory: makeMemory({
|
|
855
|
+
tags: [],
|
|
856
|
+
content: "用户喜欢深色模式",
|
|
857
|
+
}),
|
|
858
|
+
});
|
|
859
|
+
const result = isDirectAnswerEligible({
|
|
860
|
+
query: "what is 用户喜欢深色模式",
|
|
861
|
+
candidates: [candidate],
|
|
862
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.5 },
|
|
863
|
+
});
|
|
864
|
+
assert.equal(result.eligible, true);
|
|
865
|
+
assert.equal(result.reason, "eligible");
|
|
866
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.5);
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test("isDirectAnswerEligible does not require a single English prompt word before CJK terms", () => {
|
|
870
|
+
const candidate = makeCandidate({
|
|
871
|
+
memory: makeMemory({
|
|
872
|
+
tags: [],
|
|
873
|
+
content: "用户喜欢深色模式",
|
|
874
|
+
}),
|
|
875
|
+
});
|
|
876
|
+
const result = isDirectAnswerEligible({
|
|
877
|
+
query: "what 用户喜欢深色模式",
|
|
878
|
+
candidates: [candidate],
|
|
879
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.5 },
|
|
880
|
+
});
|
|
881
|
+
assert.equal(result.eligible, true);
|
|
882
|
+
assert.equal(result.reason, "eligible");
|
|
883
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.5);
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
test("isDirectAnswerEligible does not require English recall verbs before CJK terms", () => {
|
|
887
|
+
const candidate = makeCandidate({
|
|
888
|
+
memory: makeMemory({
|
|
889
|
+
tags: [],
|
|
890
|
+
content: "用户喜欢深色模式",
|
|
891
|
+
}),
|
|
892
|
+
});
|
|
893
|
+
for (const promptWord of ["find", "status", "include"]) {
|
|
894
|
+
const result = isDirectAnswerEligible({
|
|
895
|
+
query: `${promptWord} 用户喜欢深色模式`,
|
|
896
|
+
candidates: [candidate],
|
|
897
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0.5 },
|
|
898
|
+
});
|
|
899
|
+
assert.equal(result.eligible, true, promptWord);
|
|
900
|
+
assert.equal(result.reason, "eligible", promptWord);
|
|
901
|
+
assert.ok(result.tokenOverlap !== undefined && result.tokenOverlap >= 0.5, promptWord);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
|
|
353
905
|
test("isDirectAnswerEligible with tokenOverlapFloor=0 accepts any overlap (rule 45)", () => {
|
|
354
906
|
const candidate = makeCandidate({
|
|
355
907
|
memory: makeMemory({
|
|
@@ -365,6 +917,57 @@ test("isDirectAnswerEligible with tokenOverlapFloor=0 accepts any overlap (rule
|
|
|
365
917
|
assert.equal(result.eligible, true);
|
|
366
918
|
});
|
|
367
919
|
|
|
920
|
+
test("isDirectAnswerEligible rejects required CJK phrase misses when tokenOverlapFloor=0", () => {
|
|
921
|
+
const candidate = makeCandidate({
|
|
922
|
+
memory: makeMemory({
|
|
923
|
+
tags: [],
|
|
924
|
+
content: "用户讨厌深色主题",
|
|
925
|
+
}),
|
|
926
|
+
});
|
|
927
|
+
const result = isDirectAnswerEligible({
|
|
928
|
+
query: "用户喜欢深色模式",
|
|
929
|
+
candidates: [candidate],
|
|
930
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0 },
|
|
931
|
+
});
|
|
932
|
+
assert.equal(result.eligible, false);
|
|
933
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
934
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
test("isDirectAnswerEligible rejects required mixed-script misses when tokenOverlapFloor=0", () => {
|
|
938
|
+
const candidate = makeCandidate({
|
|
939
|
+
memory: makeMemory({
|
|
940
|
+
tags: [],
|
|
941
|
+
content: "客户crm降级",
|
|
942
|
+
}),
|
|
943
|
+
});
|
|
944
|
+
const result = isDirectAnswerEligible({
|
|
945
|
+
query: "客户crm升级",
|
|
946
|
+
candidates: [candidate],
|
|
947
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0 },
|
|
948
|
+
});
|
|
949
|
+
assert.equal(result.eligible, false);
|
|
950
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
951
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
test("isDirectAnswerEligible rejects required non-CJK Unicode misses when tokenOverlapFloor=0", () => {
|
|
955
|
+
const candidate = makeCandidate({
|
|
956
|
+
memory: makeMemory({
|
|
957
|
+
tags: [],
|
|
958
|
+
content: "пользователь ненавидит темный режим",
|
|
959
|
+
}),
|
|
960
|
+
});
|
|
961
|
+
const result = isDirectAnswerEligible({
|
|
962
|
+
query: "пользователь любит темный режим",
|
|
963
|
+
candidates: [candidate],
|
|
964
|
+
config: { ...DEFAULT_CONFIG, tokenOverlapFloor: 0 },
|
|
965
|
+
});
|
|
966
|
+
assert.equal(result.eligible, false);
|
|
967
|
+
assert.equal(result.reason, "below-token-overlap-floor");
|
|
968
|
+
assert.ok(result.filteredBy.includes(FILTER_LABELS.belowTokenOverlapFloor));
|
|
969
|
+
});
|
|
970
|
+
|
|
368
971
|
// ── Gate: ambiguity margin ──────────────────────────────────────────────────
|
|
369
972
|
|
|
370
973
|
test("isDirectAnswerEligible defers to hybrid when top two candidates are within ambiguity margin", () => {
|