@intentic/webq 1.359.2 → 1.361.0
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/lib/bm25.d.ts.map +1 -1
- package/dist/lib/bm25.js +22 -15
- package/dist/lib/bm25.js.map +1 -1
- package/package.json +9 -10
- package/src/lib/bm25.test.ts +16 -0
- package/src/lib/bm25.ts +23 -15
package/dist/lib/bm25.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bm25.d.ts","sourceRoot":"","sources":["../../src/lib/bm25.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW,CAAC,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAAM,EAKtB,CAAC;AAyBzB,eAAO,MAAM,QAAQ,GAAI,CAAC,UAAU,SAAS,CAAC,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,SAAS,MAAM,KAAG,WAAW,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"bm25.d.ts","sourceRoot":"","sources":["../../src/lib/bm25.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW,CAAC,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAAM,EAKtB,CAAC;AAyBzB,eAAO,MAAM,QAAQ,GAAI,CAAC,UAAU,SAAS,CAAC,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,SAAS,MAAM,KAAG,WAAW,CAAC,CAAC,CAAC,EAsC7G,CAAC"}
|
package/dist/lib/bm25.js
CHANGED
|
@@ -22,29 +22,36 @@ export const bm25Rank = (blocks, textOf, query) => {
|
|
|
22
22
|
if (queryTerms.length === 0 || blocks.length === 0) {
|
|
23
23
|
return [];
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
const avgLen = docs.reduce((sum, doc) => sum + doc.length, 0) / docs.length || 1;
|
|
25
|
+
const wanted = new Set(queryTerms);
|
|
27
26
|
const docFrequency = new Map();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
const scored = blocks.map((block, index) => {
|
|
32
|
-
const doc = docs[index] ?? [];
|
|
27
|
+
const docs = blocks.map((block) => {
|
|
28
|
+
const tokens = tokenize(textOf(block));
|
|
33
29
|
const counts = new Map();
|
|
34
|
-
for (const token of
|
|
35
|
-
|
|
30
|
+
for (const token of tokens) {
|
|
31
|
+
if (wanted.has(token)) {
|
|
32
|
+
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const term of counts.keys()) {
|
|
36
|
+
docFrequency.set(term, (docFrequency.get(term) ?? 0) + 1);
|
|
36
37
|
}
|
|
38
|
+
return { block, length: tokens.length, counts };
|
|
39
|
+
});
|
|
40
|
+
const avgLen = docs.reduce((sum, doc) => sum + doc.length, 0) / docs.length || 1;
|
|
41
|
+
const weights = queryTerms.map((term) => {
|
|
42
|
+
const df = docFrequency.get(term) ?? 0;
|
|
43
|
+
return { term, idf: df === 0 ? 0 : Math.log((docs.length - df + 0.5) / (df + 0.5) + 1) };
|
|
44
|
+
});
|
|
45
|
+
const scored = docs.map((doc) => {
|
|
37
46
|
let score = 0;
|
|
38
|
-
for (const term of
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
47
|
+
for (const { term, idf } of weights) {
|
|
48
|
+
const tf = doc.counts.get(term) ?? 0;
|
|
49
|
+
if (tf === 0) {
|
|
41
50
|
continue;
|
|
42
51
|
}
|
|
43
|
-
const idf = Math.log((docs.length - df + 0.5) / (df + 0.5) + 1);
|
|
44
|
-
const tf = counts.get(term) ?? 0;
|
|
45
52
|
score += idf * ((tf * (K1 + 1)) / (tf + K1 * (1 - B + B * (doc.length / avgLen))));
|
|
46
53
|
}
|
|
47
|
-
return { block, score };
|
|
54
|
+
return { block: doc.block, score };
|
|
48
55
|
});
|
|
49
56
|
return scored.filter((entry) => entry.score > 0).toSorted((a, b) => b.score - a.score);
|
|
50
57
|
};
|
package/dist/lib/bm25.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bm25.js","sourceRoot":"","sources":["../../src/lib/bm25.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAY,EAAE,CAC/C,IAAI;KACC,WAAW,EAAE;KACb,KAAK,CAAC,iBAAiB,CAAC;KACxB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;KACpC,GAAG,CAAC,UAAU,CAAC,CAAC;AAIzB,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE;IACzC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACpC,CAAC;IAED,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACrF,CAAC,CAAC;AAEF,MAAM,EAAE,GAAG,GAAG,CAAC;AACf,MAAM,CAAC,GAAG,IAAI,CAAC;AAMf,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAI,MAAoB,EAAE,MAA4B,EAAE,KAAa,EAAoB,EAAE;IAC/G,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,
|
|
1
|
+
{"version":3,"file":"bm25.js","sourceRoot":"","sources":["../../src/lib/bm25.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAY,EAAE,CAC/C,IAAI;KACC,WAAW,EAAE;KACb,KAAK,CAAC,iBAAiB,CAAC;KACxB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;KACpC,GAAG,CAAC,UAAU,CAAC,CAAC;AAIzB,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE;IACzC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACpC,CAAC;IAED,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACrF,CAAC,CAAC;AAEF,MAAM,EAAE,GAAG,GAAG,CAAC;AACf,MAAM,CAAC,GAAG,IAAI,CAAC;AAMf,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAI,MAAoB,EAAE,MAA4B,EAAE,KAAa,EAAoB,EAAE;IAC/G,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACpD,CAAC;QACL,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/B,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACjF,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACpC,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;IAC7F,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC;YAClC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACX,SAAS;YACb,CAAC;YACD,KAAK,IAAI,GAAG,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAC3F,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/webq",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.361.0",
|
|
4
4
|
"description": "webq, agent-native web fetch CLI: any URL as clean budgeted markdown, and bounded same-site crawls, over one shared cache",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,24 +45,23 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@intentic/agent-cli": "1.
|
|
49
|
-
"@intentic/base": "1.
|
|
48
|
+
"@intentic/agent-cli": "1.361.0",
|
|
49
|
+
"@intentic/base": "1.361.0",
|
|
50
50
|
"@stricli/core": "1.3.0",
|
|
51
51
|
"parse5": "8.0.1",
|
|
52
|
-
"playwright": "1.
|
|
53
|
-
"tslib": "2.8.1"
|
|
52
|
+
"playwright": "1.64.1"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
|
56
55
|
"@intentic/testing": "0.0.0",
|
|
57
56
|
"@intentic/tsconfig": "0.0.0",
|
|
58
|
-
"@types/bun": "1.4.
|
|
59
|
-
"@types/node": "24.19.
|
|
60
|
-
"
|
|
57
|
+
"@types/bun": "1.4.4",
|
|
58
|
+
"@types/node": "24.19.2",
|
|
59
|
+
"typescript": "7.0.2"
|
|
61
60
|
},
|
|
62
61
|
"scripts": {
|
|
63
|
-
"build": "
|
|
62
|
+
"build": "tsc",
|
|
64
63
|
"typecheck": "bun check -p tsconfig.test.json",
|
|
65
|
-
"watch": "
|
|
64
|
+
"watch": "tsc --build --watch",
|
|
66
65
|
"test": "suites",
|
|
67
66
|
"test:watch": "suites --watch"
|
|
68
67
|
}
|
package/src/lib/bm25.test.ts
CHANGED
|
@@ -30,4 +30,20 @@ describe("bm25Rank", () => {
|
|
|
30
30
|
expect(bm25Rank(blocks, (block) => block, "")).toEqual([]);
|
|
31
31
|
expect(bm25Rank(blocks, (block) => block, "zebra")).toEqual([]);
|
|
32
32
|
});
|
|
33
|
+
|
|
34
|
+
it("counts document frequency once per block and folds duplicate query terms", () => {
|
|
35
|
+
const docs = ["webhook webhook", "webhook other", ""];
|
|
36
|
+
const ranked = bm25Rank(docs, (block) => block, "webhooks webhook missing");
|
|
37
|
+
expect(ranked.map((entry) => entry.block)).toEqual(docs.slice(0, 2));
|
|
38
|
+
const idf = Math.log(1 + 1.5 / 2.5);
|
|
39
|
+
expect(ranked[0]?.score).toBeCloseTo((idf * 4.4) / (2 + 1.2 * (0.25 + 0.75 * 1.5)), 12);
|
|
40
|
+
expect(ranked[1]?.score).toBeCloseTo((idf * 2.2) / (1 + 1.2 * (0.25 + 0.75 * 1.5)), 12);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("keeps document order when scores tie and handles blocks with no tokens", () => {
|
|
44
|
+
const docs = [{ text: "webhook retry" }, { text: "retry webhook" }, { text: "!" }];
|
|
45
|
+
expect(bm25Rank(docs, (block) => block.text, "webhook").map((entry) => entry.block)).toEqual(docs.slice(0, 2));
|
|
46
|
+
expect(bm25Rank(["", "!"], (block) => block, "webhook")).toEqual([]);
|
|
47
|
+
expect(bm25Rank([], (block: string) => block, "webhook")).toEqual([]);
|
|
48
|
+
});
|
|
33
49
|
});
|
package/src/lib/bm25.ts
CHANGED
|
@@ -42,29 +42,37 @@ export const bm25Rank = <T>(blocks: readonly T[], textOf: (block: T) => string,
|
|
|
42
42
|
if (queryTerms.length === 0 || blocks.length === 0) {
|
|
43
43
|
return [];
|
|
44
44
|
}
|
|
45
|
-
const
|
|
46
|
-
const avgLen = docs.reduce((sum, doc) => sum + doc.length, 0) / docs.length || 1;
|
|
45
|
+
const wanted = new Set(queryTerms);
|
|
47
46
|
const docFrequency = new Map<string, number>();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
const scored = blocks.map((block, index) => {
|
|
52
|
-
const doc = docs[index] ?? [];
|
|
47
|
+
const docs = blocks.map((block) => {
|
|
48
|
+
const tokens = tokenize(textOf(block));
|
|
53
49
|
const counts = new Map<string, number>();
|
|
54
|
-
for (const token of
|
|
55
|
-
|
|
50
|
+
for (const token of tokens) {
|
|
51
|
+
if (wanted.has(token)) {
|
|
52
|
+
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// A repeated word still contributes only one document to its document frequency.
|
|
56
|
+
for (const term of counts.keys()) {
|
|
57
|
+
docFrequency.set(term, (docFrequency.get(term) ?? 0) + 1);
|
|
56
58
|
}
|
|
59
|
+
return { block, length: tokens.length, counts };
|
|
60
|
+
});
|
|
61
|
+
const avgLen = docs.reduce((sum, doc) => sum + doc.length, 0) / docs.length || 1;
|
|
62
|
+
const weights = queryTerms.map((term) => {
|
|
63
|
+
const df = docFrequency.get(term) ?? 0;
|
|
64
|
+
return { term, idf: df === 0 ? 0 : Math.log((docs.length - df + 0.5) / (df + 0.5) + 1) };
|
|
65
|
+
});
|
|
66
|
+
const scored = docs.map((doc) => {
|
|
57
67
|
let score = 0;
|
|
58
|
-
for (const term of
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
68
|
+
for (const { term, idf } of weights) {
|
|
69
|
+
const tf = doc.counts.get(term) ?? 0;
|
|
70
|
+
if (tf === 0) {
|
|
61
71
|
continue;
|
|
62
72
|
}
|
|
63
|
-
const idf = Math.log((docs.length - df + 0.5) / (df + 0.5) + 1);
|
|
64
|
-
const tf = counts.get(term) ?? 0;
|
|
65
73
|
score += idf * ((tf * (K1 + 1)) / (tf + K1 * (1 - B + B * (doc.length / avgLen))));
|
|
66
74
|
}
|
|
67
|
-
return { block, score };
|
|
75
|
+
return { block: doc.block, score };
|
|
68
76
|
});
|
|
69
77
|
return scored.filter((entry) => entry.score > 0).toSorted((a, b) => b.score - a.score);
|
|
70
78
|
};
|