@dzhechkov/memory 0.2.14 → 0.2.16
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/.dz-manifest.json +12 -12
- package/README.md +1 -1
- package/dist/json-backend.d.ts.map +1 -1
- package/dist/json-backend.js +40 -3
- package/dist/json-backend.js.map +1 -1
- package/dist/sqlite-backend.d.ts.map +1 -1
- package/dist/sqlite-backend.js +50 -4
- package/dist/sqlite-backend.js.map +1 -1
- package/package.json +1 -1
- package/sbom.json +16 -12
- package/src/json-backend.ts +31 -3
- package/src/sqlite-backend.ts +41 -4
package/.dz-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version":
|
|
3
|
+
"version": 3,
|
|
4
4
|
"pack": "memory",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "62355080aac189a0922afc379848a27f08abd321fa6907711cfa6190d435a753"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "dist/backend.d.ts",
|
|
@@ -97,15 +97,15 @@
|
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"path": "dist/json-backend.d.ts.map",
|
|
100
|
-
"sha256": "
|
|
100
|
+
"sha256": "95adb5287a060d417ed83d2364d8f63b973790e9885dc29ea7182132f4231b7c"
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"path": "dist/json-backend.js",
|
|
104
|
-
"sha256": "
|
|
104
|
+
"sha256": "d53fcda53b7a1d24ccff4c3e1d98794985a9de01c97f6c357eec91ad64c2a341"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"path": "dist/json-backend.js.map",
|
|
108
|
-
"sha256": "
|
|
108
|
+
"sha256": "7e0a073bfc25ae6fea53f29ad518319c31cdac070a1215bfb8a7584cb7ce9c33"
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"path": "dist/reflexion.d.ts",
|
|
@@ -129,15 +129,15 @@
|
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"path": "dist/sqlite-backend.d.ts.map",
|
|
132
|
-
"sha256": "
|
|
132
|
+
"sha256": "784903497cd0ec1e84358c3b96cfc36e34d9077a7626351f8792bc69ad735f68"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"path": "dist/sqlite-backend.js",
|
|
136
|
-
"sha256": "
|
|
136
|
+
"sha256": "ebff426dfa8028097fbdcc350ba698d3c5472dc881dfc82604d8e23408d6f00d"
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
"path": "dist/sqlite-backend.js.map",
|
|
140
|
-
"sha256": "
|
|
140
|
+
"sha256": "92b0105282fb9069efde0fcb45d5b91c4ae0fd7e65afbdb077443ea9fec0f551"
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"path": "dist/sqlite-probe.d.ts",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
"path": "package.json",
|
|
160
|
-
"sha256": "
|
|
160
|
+
"sha256": "b5460cbb3562c5f7afa15aca275164716bc24c0e38f28a983b14c90e954dea17"
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"path": "src/backend.ts",
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
},
|
|
182
182
|
{
|
|
183
183
|
"path": "src/json-backend.ts",
|
|
184
|
-
"sha256": "
|
|
184
|
+
"sha256": "ba881b55f48dabe7d95ce87ee1c840180850899f551173cd5940e54a8d6c6cf6"
|
|
185
185
|
},
|
|
186
186
|
{
|
|
187
187
|
"path": "src/reflexion.ts",
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
},
|
|
190
190
|
{
|
|
191
191
|
"path": "src/sqlite-backend.ts",
|
|
192
|
-
"sha256": "
|
|
192
|
+
"sha256": "b2f4195a9db186e788aebafb50734078c2eec5cf74fdb45dc53ca6ec4d0208e1"
|
|
193
193
|
},
|
|
194
194
|
{
|
|
195
195
|
"path": "src/sqlite-probe.ts",
|
|
@@ -197,5 +197,5 @@
|
|
|
197
197
|
}
|
|
198
198
|
]
|
|
199
199
|
},
|
|
200
|
-
"signature": "
|
|
200
|
+
"signature": "yS2k1owU1LLdUxrtaBCqJqmuTC7cVPusxFlc2cRsIMQRH+MMgh12rGci5YKuGH3QKCAgbZGeUuxaWTh9NKVWBA=="
|
|
201
201
|
}
|
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ are intentionally *not* hard dependencies — see
|
|
|
50
50
|
|
|
51
51
|
## Status
|
|
52
52
|
|
|
53
|
-
`0.2.
|
|
53
|
+
`0.2.16` — a signature-only republish; `0.2.11` shipped with a stale manifest. No behaviour changes.
|
|
54
54
|
|
|
55
55
|
`0.2.11` — a query that matched nothing returns nothing (see above). `0.2.10` — the lexical tokenizer is Unicode-aware. It split on `[^a-z0-9]+`, so every non-Latin letter
|
|
56
56
|
was a separator and a Cyrillic query produced **zero terms**: the FTS5 branch was skipped, relevance
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-backend.d.ts","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"json-backend.d.ts","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAmE7E,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,sFAAsF;AACtF,qBAAa,eAAgB,YAAW,aAAa;IACnD,QAAQ,CAAC,IAAI,eAAe;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAElC,OAAO,GAAE,sBAA2B;IAIhD,6EAA6E;WAChE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAM7D;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAMlD,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIlD,4EAA4E;IAC5E,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,EAAE;IAwB7C,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,+CAA+C;IAC/C,OAAO,IAAI,YAAY,EAAE;IAIzB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,gFAAgF;IAChF,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI5B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB;;;;;;;OAOG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAerB,qFAAqF;IACrF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAKrB,gDAAgD;IAChD,QAAQ,IAAI,IAAI;CAMjB"}
|
package/dist/json-backend.js
CHANGED
|
@@ -33,15 +33,52 @@ function tokenize(text) {
|
|
|
33
33
|
// 2026-08-21). The promise was "the alphabet, not the thresholds"; this keeps it.
|
|
34
34
|
.filter((token) => [...token].length > 1);
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Crude prefix-stem for morphology-bearing languages — feature recall-ru-morphology.
|
|
38
|
+
*
|
|
39
|
+
* MEASURED 2026-08-24: one store, teach «…случай миопатии…» — `recall "миопатия"` returned [].
|
|
40
|
+
* FTS5's unicode61 does no Russian stemming, so nominative vs genitive never match, and 63% of
|
|
41
|
+
* real recall traffic is Cyrillic; the July build only «found» such queries because its ASCII
|
|
42
|
+
* tokenizer produced ZERO tokens and a token-less query returned the whole store (82757da0 closed
|
|
43
|
+
* that). The cure is a PREFIX: `миопати*` covers миопатия/миопатии/миопатию alike.
|
|
44
|
+
*
|
|
45
|
+
* ≥6 code points → drop 2; ==5 → drop 1; shorter → no stem (a 4-letter prefix of a 4-letter word
|
|
46
|
+
* is the word). Deliberately NOT a stemmer: root alternations (бежать/бегу) stay uncovered — the
|
|
47
|
+
* honest scope; semantic coverage belongs to the vector tier where one is mirrored.
|
|
48
|
+
*/
|
|
49
|
+
function stemOf(token) {
|
|
50
|
+
const cps = [...token];
|
|
51
|
+
if (cps.length >= 6)
|
|
52
|
+
return cps.slice(0, -2).join('');
|
|
53
|
+
if (cps.length === 5)
|
|
54
|
+
return cps.slice(0, -1).join('');
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/** Count how many query terms appear in a record's text/skillId. Exact hit = 1; a hit only via
|
|
58
|
+
* the prefix-stem = 0.7 (a real signal, ranked below an exact word — and above the >0 filter). */
|
|
37
59
|
function relevanceOf(record, terms) {
|
|
38
60
|
if (terms.length === 0)
|
|
39
61
|
return 0;
|
|
40
|
-
const
|
|
62
|
+
const tokens = tokenize(`${record.text} ${record.skillId}`);
|
|
63
|
+
const haystack = new Set(tokens);
|
|
41
64
|
let hits = 0;
|
|
42
65
|
for (const term of terms) {
|
|
43
|
-
if (haystack.has(term))
|
|
66
|
+
if (haystack.has(term)) {
|
|
44
67
|
hits += 1;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const stem = stemOf(term);
|
|
71
|
+
if (stem !== null) {
|
|
72
|
+
let found = false;
|
|
73
|
+
for (const t of tokens) {
|
|
74
|
+
if (t.startsWith(stem)) {
|
|
75
|
+
found = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (found)
|
|
80
|
+
hits += 0.7;
|
|
81
|
+
}
|
|
45
82
|
}
|
|
46
83
|
return hits;
|
|
47
84
|
}
|
package/dist/json-backend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-backend.js","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;;;;;;;;GAYG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI;SACR,WAAW,EAAE;SACb,KAAK,CAAC,iBAAiB,CAAC;QACzB,8FAA8F;QAC9F,iGAAiG;QACjG,0FAA0F;QAC1F,kFAAkF;SACjF,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"json-backend.js","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-backend.d.ts","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-backend.d.ts","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkI7E,iCAAiC;AACjC,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAM;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkB;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,EAAE,EAAE,GAAG;IAyBnB,4DAA4D;IAC5D,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa;IAW5C,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIlD;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,EAAE;IAqE7C,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,6CAA6C;IAC7C,OAAO,IAAI,YAAY,EAAE;IAIzB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,sIAAsI;IACtI,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI5B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,oEAAoE;IACpE,OAAO,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAiB/C,qCAAqC;IACrC,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/sqlite-backend.js
CHANGED
|
@@ -35,15 +35,52 @@ function tokenize(text) {
|
|
|
35
35
|
// 2026-08-21). The promise was "the alphabet, not the thresholds"; this keeps it.
|
|
36
36
|
.filter((token) => [...token].length > 1);
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Crude prefix-stem for morphology-bearing languages — feature recall-ru-morphology.
|
|
40
|
+
*
|
|
41
|
+
* MEASURED 2026-08-24: one store, teach «…случай миопатии…» — `recall "миопатия"` returned [].
|
|
42
|
+
* FTS5's unicode61 does no Russian stemming, so nominative vs genitive never match, and 63% of
|
|
43
|
+
* real recall traffic is Cyrillic; the July build only «found» such queries because its ASCII
|
|
44
|
+
* tokenizer produced ZERO tokens and a token-less query returned the whole store (82757da0 closed
|
|
45
|
+
* that). The cure is a PREFIX: `миопати*` covers миопатия/миопатии/миопатию alike.
|
|
46
|
+
*
|
|
47
|
+
* ≥6 code points → drop 2; ==5 → drop 1; shorter → no stem (a 4-letter prefix of a 4-letter word
|
|
48
|
+
* is the word). Deliberately NOT a stemmer: root alternations (бежать/бегу) stay uncovered — the
|
|
49
|
+
* honest scope; semantic coverage belongs to the vector tier where one is mirrored.
|
|
50
|
+
*/
|
|
51
|
+
function stemOf(token) {
|
|
52
|
+
const cps = [...token];
|
|
53
|
+
if (cps.length >= 6)
|
|
54
|
+
return cps.slice(0, -2).join('');
|
|
55
|
+
if (cps.length === 5)
|
|
56
|
+
return cps.slice(0, -1).join('');
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/** Count how many query terms appear in a record's text/skillId. Exact hit = 1; a hit only via
|
|
60
|
+
* the prefix-stem = 0.7 (a real signal, ranked below an exact word — and above the >0 filter). */
|
|
39
61
|
function relevanceOf(record, terms) {
|
|
40
62
|
if (terms.length === 0)
|
|
41
63
|
return 0;
|
|
42
|
-
const
|
|
64
|
+
const tokens = tokenize(`${record.text} ${record.skillId}`);
|
|
65
|
+
const haystack = new Set(tokens);
|
|
43
66
|
let hits = 0;
|
|
44
67
|
for (const term of terms) {
|
|
45
|
-
if (haystack.has(term))
|
|
68
|
+
if (haystack.has(term)) {
|
|
46
69
|
hits += 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const stem = stemOf(term);
|
|
73
|
+
if (stem !== null) {
|
|
74
|
+
let found = false;
|
|
75
|
+
for (const t of tokens) {
|
|
76
|
+
if (t.startsWith(stem)) {
|
|
77
|
+
found = true;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (found)
|
|
82
|
+
hits += 0.7;
|
|
83
|
+
}
|
|
47
84
|
}
|
|
48
85
|
return hits;
|
|
49
86
|
}
|
|
@@ -169,7 +206,16 @@ export class SqliteBackend {
|
|
|
169
206
|
if (this.hasFts5 && query.text !== undefined && query.text.trim().length > 0) {
|
|
170
207
|
try {
|
|
171
208
|
// FTS5 query syntax: simple terms joined by spaces (implicit AND → OR with ranking)
|
|
172
|
-
|
|
209
|
+
// Each token also contributes its prefix-stem as `stem*` (see stemOf): the exact word and
|
|
210
|
+
// its inflections all match, and a row holding the exact token matches BOTH disjuncts, so
|
|
211
|
+
// bm25 ranks it at or above a prefix-only row. Tokens are \p{L}\p{N}-only — safe to
|
|
212
|
+
// interpolate; the star is appended HERE, never taken from user text.
|
|
213
|
+
const ftsQuery = tokenize(query.text)
|
|
214
|
+
.flatMap((t) => {
|
|
215
|
+
const stem = stemOf(t);
|
|
216
|
+
return stem === null ? [t] : [t, stem + '*'];
|
|
217
|
+
})
|
|
218
|
+
.join(' OR ');
|
|
173
219
|
if (ftsQuery.length > 0) {
|
|
174
220
|
let rows;
|
|
175
221
|
if (query.skillId !== undefined) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-backend.js","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;;;;;;;;GAYG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI;SACR,WAAW,EAAE;SACb,KAAK,CAAC,iBAAiB,CAAC;QACzB,8FAA8F;QAC9F,iGAAiG;QACjG,0FAA0F;QAC1F,kFAAkF;SACjF,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"sqlite-backend.js","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/sbom.json
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"hashes": [
|
|
26
26
|
{
|
|
27
27
|
"alg": "SHA-256",
|
|
28
|
-
"content": "
|
|
28
|
+
"content": "62355080aac189a0922afc379848a27f08abd321fa6907711cfa6190d435a753"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
},
|
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
"hashes": [
|
|
246
246
|
{
|
|
247
247
|
"alg": "SHA-256",
|
|
248
|
-
"content": "
|
|
248
|
+
"content": "95adb5287a060d417ed83d2364d8f63b973790e9885dc29ea7182132f4231b7c"
|
|
249
249
|
}
|
|
250
250
|
]
|
|
251
251
|
},
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
"hashes": [
|
|
256
256
|
{
|
|
257
257
|
"alg": "SHA-256",
|
|
258
|
-
"content": "
|
|
258
|
+
"content": "d53fcda53b7a1d24ccff4c3e1d98794985a9de01c97f6c357eec91ad64c2a341"
|
|
259
259
|
}
|
|
260
260
|
]
|
|
261
261
|
},
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
"hashes": [
|
|
266
266
|
{
|
|
267
267
|
"alg": "SHA-256",
|
|
268
|
-
"content": "
|
|
268
|
+
"content": "7e0a073bfc25ae6fea53f29ad518319c31cdac070a1215bfb8a7584cb7ce9c33"
|
|
269
269
|
}
|
|
270
270
|
]
|
|
271
271
|
},
|
|
@@ -325,7 +325,7 @@
|
|
|
325
325
|
"hashes": [
|
|
326
326
|
{
|
|
327
327
|
"alg": "SHA-256",
|
|
328
|
-
"content": "
|
|
328
|
+
"content": "784903497cd0ec1e84358c3b96cfc36e34d9077a7626351f8792bc69ad735f68"
|
|
329
329
|
}
|
|
330
330
|
]
|
|
331
331
|
},
|
|
@@ -335,7 +335,7 @@
|
|
|
335
335
|
"hashes": [
|
|
336
336
|
{
|
|
337
337
|
"alg": "SHA-256",
|
|
338
|
-
"content": "
|
|
338
|
+
"content": "ebff426dfa8028097fbdcc350ba698d3c5472dc881dfc82604d8e23408d6f00d"
|
|
339
339
|
}
|
|
340
340
|
]
|
|
341
341
|
},
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
"hashes": [
|
|
346
346
|
{
|
|
347
347
|
"alg": "SHA-256",
|
|
348
|
-
"content": "
|
|
348
|
+
"content": "92b0105282fb9069efde0fcb45d5b91c4ae0fd7e65afbdb077443ea9fec0f551"
|
|
349
349
|
}
|
|
350
350
|
]
|
|
351
351
|
},
|
|
@@ -392,10 +392,14 @@
|
|
|
392
392
|
{
|
|
393
393
|
"type": "file",
|
|
394
394
|
"name": "package.json",
|
|
395
|
-
"
|
|
395
|
+
"properties": [
|
|
396
396
|
{
|
|
397
|
-
"
|
|
398
|
-
"
|
|
397
|
+
"name": "dz:digest-basis",
|
|
398
|
+
"value": "package-json-ordered-conditions-v2"
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
"name": "dz:canonical-json-sha256-v2",
|
|
402
|
+
"value": "b5460cbb3562c5f7afa15aca275164716bc24c0e38f28a983b14c90e954dea17"
|
|
399
403
|
}
|
|
400
404
|
]
|
|
401
405
|
},
|
|
@@ -455,7 +459,7 @@
|
|
|
455
459
|
"hashes": [
|
|
456
460
|
{
|
|
457
461
|
"alg": "SHA-256",
|
|
458
|
-
"content": "
|
|
462
|
+
"content": "ba881b55f48dabe7d95ce87ee1c840180850899f551173cd5940e54a8d6c6cf6"
|
|
459
463
|
}
|
|
460
464
|
]
|
|
461
465
|
},
|
|
@@ -475,7 +479,7 @@
|
|
|
475
479
|
"hashes": [
|
|
476
480
|
{
|
|
477
481
|
"alg": "SHA-256",
|
|
478
|
-
"content": "
|
|
482
|
+
"content": "b2f4195a9db186e788aebafb50734078c2eec5cf74fdb45dc53ca6ec4d0208e1"
|
|
479
483
|
}
|
|
480
484
|
]
|
|
481
485
|
},
|
package/src/json-backend.ts
CHANGED
|
@@ -39,13 +39,41 @@ function tokenize(text: string): string[] {
|
|
|
39
39
|
.filter((token) => [...token].length > 1);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Crude prefix-stem for morphology-bearing languages — feature recall-ru-morphology.
|
|
44
|
+
*
|
|
45
|
+
* MEASURED 2026-08-24: one store, teach «…случай миопатии…» — `recall "миопатия"` returned [].
|
|
46
|
+
* FTS5's unicode61 does no Russian stemming, so nominative vs genitive never match, and 63% of
|
|
47
|
+
* real recall traffic is Cyrillic; the July build only «found» such queries because its ASCII
|
|
48
|
+
* tokenizer produced ZERO tokens and a token-less query returned the whole store (82757da0 closed
|
|
49
|
+
* that). The cure is a PREFIX: `миопати*` covers миопатия/миопатии/миопатию alike.
|
|
50
|
+
*
|
|
51
|
+
* ≥6 code points → drop 2; ==5 → drop 1; shorter → no stem (a 4-letter prefix of a 4-letter word
|
|
52
|
+
* is the word). Deliberately NOT a stemmer: root alternations (бежать/бегу) stay uncovered — the
|
|
53
|
+
* honest scope; semantic coverage belongs to the vector tier where one is mirrored.
|
|
54
|
+
*/
|
|
55
|
+
function stemOf(token: string): string | null {
|
|
56
|
+
const cps = [...token];
|
|
57
|
+
if (cps.length >= 6) return cps.slice(0, -2).join('');
|
|
58
|
+
if (cps.length === 5) return cps.slice(0, -1).join('');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Count how many query terms appear in a record's text/skillId. Exact hit = 1; a hit only via
|
|
63
|
+
* the prefix-stem = 0.7 (a real signal, ranked below an exact word — and above the >0 filter). */
|
|
43
64
|
function relevanceOf(record: MemoryRecord, terms: readonly string[]): number {
|
|
44
65
|
if (terms.length === 0) return 0;
|
|
45
|
-
const
|
|
66
|
+
const tokens = tokenize(`${record.text} ${record.skillId}`);
|
|
67
|
+
const haystack = new Set(tokens);
|
|
46
68
|
let hits = 0;
|
|
47
69
|
for (const term of terms) {
|
|
48
|
-
if (haystack.has(term)) hits += 1;
|
|
70
|
+
if (haystack.has(term)) { hits += 1; continue; }
|
|
71
|
+
const stem = stemOf(term);
|
|
72
|
+
if (stem !== null) {
|
|
73
|
+
let found = false;
|
|
74
|
+
for (const t of tokens) { if (t.startsWith(stem)) { found = true; break; } }
|
|
75
|
+
if (found) hits += 0.7;
|
|
76
|
+
}
|
|
49
77
|
}
|
|
50
78
|
return hits;
|
|
51
79
|
}
|
package/src/sqlite-backend.ts
CHANGED
|
@@ -42,13 +42,41 @@ function tokenize(text: string): string[] {
|
|
|
42
42
|
.filter((token) => [...token].length > 1);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Crude prefix-stem for morphology-bearing languages — feature recall-ru-morphology.
|
|
47
|
+
*
|
|
48
|
+
* MEASURED 2026-08-24: one store, teach «…случай миопатии…» — `recall "миопатия"` returned [].
|
|
49
|
+
* FTS5's unicode61 does no Russian stemming, so nominative vs genitive never match, and 63% of
|
|
50
|
+
* real recall traffic is Cyrillic; the July build only «found» such queries because its ASCII
|
|
51
|
+
* tokenizer produced ZERO tokens and a token-less query returned the whole store (82757da0 closed
|
|
52
|
+
* that). The cure is a PREFIX: `миопати*` covers миопатия/миопатии/миопатию alike.
|
|
53
|
+
*
|
|
54
|
+
* ≥6 code points → drop 2; ==5 → drop 1; shorter → no stem (a 4-letter prefix of a 4-letter word
|
|
55
|
+
* is the word). Deliberately NOT a stemmer: root alternations (бежать/бегу) stay uncovered — the
|
|
56
|
+
* honest scope; semantic coverage belongs to the vector tier where one is mirrored.
|
|
57
|
+
*/
|
|
58
|
+
function stemOf(token: string): string | null {
|
|
59
|
+
const cps = [...token];
|
|
60
|
+
if (cps.length >= 6) return cps.slice(0, -2).join('');
|
|
61
|
+
if (cps.length === 5) return cps.slice(0, -1).join('');
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Count how many query terms appear in a record's text/skillId. Exact hit = 1; a hit only via
|
|
66
|
+
* the prefix-stem = 0.7 (a real signal, ranked below an exact word — and above the >0 filter). */
|
|
46
67
|
function relevanceOf(record: MemoryRecord, terms: readonly string[]): number {
|
|
47
68
|
if (terms.length === 0) return 0;
|
|
48
|
-
const
|
|
69
|
+
const tokens = tokenize(`${record.text} ${record.skillId}`);
|
|
70
|
+
const haystack = new Set(tokens);
|
|
49
71
|
let hits = 0;
|
|
50
72
|
for (const term of terms) {
|
|
51
|
-
if (haystack.has(term)) hits += 1;
|
|
73
|
+
if (haystack.has(term)) { hits += 1; continue; }
|
|
74
|
+
const stem = stemOf(term);
|
|
75
|
+
if (stem !== null) {
|
|
76
|
+
let found = false;
|
|
77
|
+
for (const t of tokens) { if (t.startsWith(stem)) { found = true; break; } }
|
|
78
|
+
if (found) hits += 0.7;
|
|
79
|
+
}
|
|
52
80
|
}
|
|
53
81
|
return hits;
|
|
54
82
|
}
|
|
@@ -205,7 +233,16 @@ export class SqliteBackend implements MemoryBackend {
|
|
|
205
233
|
if (this.hasFts5 && query.text !== undefined && query.text.trim().length > 0) {
|
|
206
234
|
try {
|
|
207
235
|
// FTS5 query syntax: simple terms joined by spaces (implicit AND → OR with ranking)
|
|
208
|
-
|
|
236
|
+
// Each token also contributes its prefix-stem as `stem*` (see stemOf): the exact word and
|
|
237
|
+
// its inflections all match, and a row holding the exact token matches BOTH disjuncts, so
|
|
238
|
+
// bm25 ranks it at or above a prefix-only row. Tokens are \p{L}\p{N}-only — safe to
|
|
239
|
+
// interpolate; the star is appended HERE, never taken from user text.
|
|
240
|
+
const ftsQuery = tokenize(query.text)
|
|
241
|
+
.flatMap((t) => {
|
|
242
|
+
const stem = stemOf(t);
|
|
243
|
+
return stem === null ? [t] : [t, stem + '*'];
|
|
244
|
+
})
|
|
245
|
+
.join(' OR ');
|
|
209
246
|
if (ftsQuery.length > 0) {
|
|
210
247
|
let rows: any[];
|
|
211
248
|
if (query.skillId !== undefined) {
|