@kognai/tool-router 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +47 -2
- package/dist/adapters/awesome-list.d.ts +10 -0
- package/dist/adapters/awesome-list.js +68 -0
- package/dist/adapters/mcp-registry.d.ts +12 -0
- package/dist/adapters/mcp-registry.js +51 -0
- package/dist/adapters/mcp-tools.d.ts +49 -0
- package/dist/adapters/mcp-tools.js +60 -0
- package/dist/adapters/openapi.d.ts +25 -0
- package/dist/adapters/openapi.js +107 -0
- package/dist/adapters/pay-skills.d.ts +13 -0
- package/dist/adapters/pay-skills.js +119 -0
- package/dist/adapters/raw.d.ts +82 -0
- package/dist/adapters/raw.js +5 -0
- package/dist/adapters/x402-discovery.d.ts +6 -0
- package/dist/adapters/x402-discovery.js +100 -0
- package/dist/catalog/file-store.d.ts +27 -0
- package/dist/catalog/file-store.js +102 -0
- package/dist/catalog/ingest.d.ts +53 -0
- package/dist/catalog/ingest.js +276 -0
- package/dist/catalog/lifecycle.d.ts +24 -0
- package/dist/catalog/lifecycle.js +51 -0
- package/dist/catalog/memory-store.d.ts +49 -0
- package/dist/catalog/memory-store.js +92 -0
- package/dist/catalog/risk.d.ts +22 -0
- package/dist/catalog/risk.js +73 -0
- package/dist/catalog/sanitise.d.ts +17 -0
- package/dist/catalog/sanitise.js +109 -0
- package/dist/classify/classifier.d.ts +12 -0
- package/dist/classify/classifier.js +52 -0
- package/dist/classify/decompose.d.ts +10 -0
- package/dist/classify/decompose.js +107 -0
- package/dist/classify/rules.d.ts +46 -0
- package/dist/classify/rules.js +249 -0
- package/dist/classify/tagger.d.ts +5 -0
- package/dist/classify/tagger.js +29 -0
- package/dist/classify/taxonomy.d.ts +31 -0
- package/dist/classify/taxonomy.js +221 -0
- package/dist/classify/text.d.ts +9 -0
- package/dist/classify/text.js +38 -0
- package/dist/eligibility/engine.d.ts +11 -0
- package/dist/eligibility/engine.js +19 -0
- package/dist/eligibility/rules.d.ts +33 -0
- package/dist/eligibility/rules.js +121 -0
- package/dist/flags.d.ts +33 -0
- package/dist/flags.js +34 -0
- package/dist/hash.d.ts +8 -0
- package/dist/hash.js +30 -0
- package/dist/ids.d.ts +17 -0
- package/dist/ids.js +54 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +95 -0
- package/dist/money.d.ts +10 -0
- package/dist/money.js +41 -0
- package/dist/rank/confidence.d.ts +15 -0
- package/dist/rank/confidence.js +48 -0
- package/dist/rank/ranker.d.ts +34 -0
- package/dist/rank/ranker.js +81 -0
- package/dist/rank/weights.d.ts +40 -0
- package/dist/rank/weights.js +46 -0
- package/dist/retrieve/bm25.d.ts +21 -0
- package/dist/retrieve/bm25.js +59 -0
- package/dist/retrieve/capability.d.ts +1 -0
- package/dist/retrieve/capability.js +13 -0
- package/dist/retrieve/hash-embed.d.ts +4 -0
- package/dist/retrieve/hash-embed.js +41 -0
- package/dist/retrieve/hybrid.d.ts +74 -0
- package/dist/retrieve/hybrid.js +125 -0
- package/dist/retrieve/schema-fit.d.ts +9 -0
- package/dist/retrieve/schema-fit.js +38 -0
- package/dist/retrieve/tokenise.d.ts +2 -0
- package/dist/retrieve/tokenise.js +51 -0
- package/dist/retrieve/vector.d.ts +18 -0
- package/dist/retrieve/vector.js +47 -0
- package/dist/router.d.ts +37 -0
- package/dist/router.js +114 -0
- package/dist/schema/tool.schema.json +117 -0
- package/dist/schema/validate.d.ts +9 -0
- package/dist/schema/validate.js +92 -0
- package/dist/shortlist/generate.d.ts +21 -0
- package/dist/shortlist/generate.js +75 -0
- package/dist/types/ports.d.ts +71 -0
- package/dist/types/ports.js +2 -0
- package/dist/types/route.d.ts +121 -0
- package/dist/types/route.js +5 -0
- package/dist/types/tool.d.ts +123 -0
- package/dist/types/tool.js +17 -0
- package/package.json +39 -4
package/dist/ids.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool ids (ADR-004): tool:<origin>:<namespace>/<name>[#<operation>]. Lower case, version free, URL and file safe.
|
|
3
|
+
*/
|
|
4
|
+
import type { Origin } from './types/tool';
|
|
5
|
+
export declare const TOOL_ID_RE: RegExp;
|
|
6
|
+
export interface ParsedToolId {
|
|
7
|
+
origin: Origin;
|
|
8
|
+
path: string;
|
|
9
|
+
op?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseToolId(s: string): ParsedToolId | null;
|
|
12
|
+
/** Normalises any string to one id segment: lower case, [a-z0-9._-], no leading or trailing separators. */
|
|
13
|
+
export declare function slugSegment(s: string): string;
|
|
14
|
+
export declare function formatToolId(origin: Origin, path: string, op?: string): string;
|
|
15
|
+
/** The id without '#operation'. */
|
|
16
|
+
export declare const baseId: (id: string) => string;
|
|
17
|
+
export declare const withOp: (id: string, op: string) => string;
|
package/dist/ids.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withOp = exports.baseId = exports.TOOL_ID_RE = void 0;
|
|
4
|
+
exports.parseToolId = parseToolId;
|
|
5
|
+
exports.slugSegment = slugSegment;
|
|
6
|
+
exports.formatToolId = formatToolId;
|
|
7
|
+
exports.TOOL_ID_RE = /^tool:(kognai|mcp|openapi|x402|community|composio):[a-z0-9._-]+(\/[a-z0-9._-]+)+(#[a-z0-9_.-]+)?$/;
|
|
8
|
+
const SEG_RE = /^[a-z0-9._-]+$/;
|
|
9
|
+
const MAX_PATH = 128;
|
|
10
|
+
function parseToolId(s) {
|
|
11
|
+
if (typeof s !== 'string' || !exports.TOOL_ID_RE.test(s))
|
|
12
|
+
return null;
|
|
13
|
+
const rest = s.slice(5);
|
|
14
|
+
const colon = rest.indexOf(':');
|
|
15
|
+
const origin = rest.slice(0, colon);
|
|
16
|
+
let path = rest.slice(colon + 1);
|
|
17
|
+
let op;
|
|
18
|
+
const hash = path.indexOf('#');
|
|
19
|
+
if (hash >= 0) {
|
|
20
|
+
op = path.slice(hash + 1);
|
|
21
|
+
path = path.slice(0, hash);
|
|
22
|
+
}
|
|
23
|
+
if (path.length > MAX_PATH)
|
|
24
|
+
return null;
|
|
25
|
+
if (path.split('/').some((seg) => seg === '' || seg === '.' || seg === '..'))
|
|
26
|
+
return null;
|
|
27
|
+
return op === undefined ? { origin, path } : { origin, path, op };
|
|
28
|
+
}
|
|
29
|
+
/** Normalises any string to one id segment: lower case, [a-z0-9._-], no leading or trailing separators. */
|
|
30
|
+
function slugSegment(s) {
|
|
31
|
+
const out = String(s ?? '')
|
|
32
|
+
.normalize('NFKD').replace(/[̀-ͯ]/g, '')
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
35
|
+
.replace(/-{2,}/g, '-')
|
|
36
|
+
.replace(/^[-.]+|[-.]+$/g, '');
|
|
37
|
+
return out.slice(0, 60) || 'x';
|
|
38
|
+
}
|
|
39
|
+
function formatToolId(origin, path, op) {
|
|
40
|
+
const segs = path.split('/').map(slugSegment);
|
|
41
|
+
if (segs.length < 2)
|
|
42
|
+
throw new Error(`tool id path needs namespace/name, got "${path}"`);
|
|
43
|
+
if (!segs.every((x) => SEG_RE.test(x)))
|
|
44
|
+
throw new Error(`bad tool id path "${path}"`);
|
|
45
|
+
const id = `tool:${origin}:${segs.join('/')}${op ? `#${slugSegment(op).replace(/-/g, '_')}` : ''}`;
|
|
46
|
+
if (!parseToolId(id))
|
|
47
|
+
throw new Error(`could not form a valid tool id from "${path}"`);
|
|
48
|
+
return id;
|
|
49
|
+
}
|
|
50
|
+
/** The id without '#operation'. */
|
|
51
|
+
const baseId = (id) => id.split('#')[0];
|
|
52
|
+
exports.baseId = baseId;
|
|
53
|
+
const withOp = (id, op) => `${(0, exports.baseId)(id)}#${op}`;
|
|
54
|
+
exports.withOp = withOp;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** @kognai/tool-router public exports (published under MIT, Founder 2026-11-12; was private, decision D1). */
|
|
2
|
+
export * from './types/tool';
|
|
3
|
+
export * from './types/route';
|
|
4
|
+
export * from './types/ports';
|
|
5
|
+
export * from './ids';
|
|
6
|
+
export * from './hash';
|
|
7
|
+
export * from './money';
|
|
8
|
+
export * from './flags';
|
|
9
|
+
export * from './schema/validate';
|
|
10
|
+
export * from './catalog/sanitise';
|
|
11
|
+
export * from './catalog/lifecycle';
|
|
12
|
+
export * from './catalog/risk';
|
|
13
|
+
export * from './catalog/ingest';
|
|
14
|
+
export * from './catalog/memory-store';
|
|
15
|
+
export * from './catalog/file-store';
|
|
16
|
+
export * from './adapters/raw';
|
|
17
|
+
export { fromServerJson } from './adapters/mcp-registry';
|
|
18
|
+
export { fromMcpToolsList, listToolsLive, type ApprovedServer } from './adapters/mcp-tools';
|
|
19
|
+
export { fromOpenApi } from './adapters/openapi';
|
|
20
|
+
export { fromPaySkills } from './adapters/pay-skills';
|
|
21
|
+
export { fromX402Resources } from './adapters/x402-discovery';
|
|
22
|
+
export { fromAwesomeMarkdown } from './adapters/awesome-list';
|
|
23
|
+
export * from './classify/taxonomy';
|
|
24
|
+
export { classifyRules } from './classify/rules';
|
|
25
|
+
export { classify } from './classify/classifier';
|
|
26
|
+
export { decompose, topoOrder } from './classify/decompose';
|
|
27
|
+
export { tagCapabilities } from './classify/tagger';
|
|
28
|
+
export { tokenise } from './retrieve/tokenise';
|
|
29
|
+
export { Bm25Index } from './retrieve/bm25';
|
|
30
|
+
export { topK, cosineAll } from './retrieve/vector';
|
|
31
|
+
export { hashEmbed, hashEmbeddingPort, HASH_MODEL } from './retrieve/hash-embed';
|
|
32
|
+
export { RouterIndex, retrieve, docText, CALIBRATION } from './retrieve/hybrid';
|
|
33
|
+
export { firstFailure, authNeeded, RULE_IDS, RULE_LABEL } from './eligibility/rules';
|
|
34
|
+
export { filter } from './eligibility/engine';
|
|
35
|
+
export { wilsonLower, successScore } from './rank/confidence';
|
|
36
|
+
export { DEFAULT_WEIGHTS, DEFAULT_FIT, RELEVANCE_FLOOR, AIAX_READY_WEIGHT, checkWeights } from './rank/weights';
|
|
37
|
+
export { rank } from './rank/ranker';
|
|
38
|
+
export { generate, MAX_CANDIDATES } from './shortlist/generate';
|
|
39
|
+
export { route, type RouterDeps, type RouterConfig } from './router';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.route = exports.MAX_CANDIDATES = exports.generate = exports.rank = exports.checkWeights = exports.AIAX_READY_WEIGHT = exports.RELEVANCE_FLOOR = exports.DEFAULT_FIT = exports.DEFAULT_WEIGHTS = exports.successScore = exports.wilsonLower = exports.filter = exports.RULE_LABEL = exports.RULE_IDS = exports.authNeeded = exports.firstFailure = exports.CALIBRATION = exports.docText = exports.retrieve = exports.RouterIndex = exports.HASH_MODEL = exports.hashEmbeddingPort = exports.hashEmbed = exports.cosineAll = exports.topK = exports.Bm25Index = exports.tokenise = exports.tagCapabilities = exports.topoOrder = exports.decompose = exports.classify = exports.classifyRules = exports.fromAwesomeMarkdown = exports.fromX402Resources = exports.fromPaySkills = exports.fromOpenApi = exports.listToolsLive = exports.fromMcpToolsList = exports.fromServerJson = void 0;
|
|
18
|
+
/** @kognai/tool-router public exports (published under MIT, Founder 2026-11-12; was private, decision D1). */
|
|
19
|
+
__exportStar(require("./types/tool"), exports);
|
|
20
|
+
__exportStar(require("./types/route"), exports);
|
|
21
|
+
__exportStar(require("./types/ports"), exports);
|
|
22
|
+
__exportStar(require("./ids"), exports);
|
|
23
|
+
__exportStar(require("./hash"), exports);
|
|
24
|
+
__exportStar(require("./money"), exports);
|
|
25
|
+
__exportStar(require("./flags"), exports);
|
|
26
|
+
__exportStar(require("./schema/validate"), exports);
|
|
27
|
+
__exportStar(require("./catalog/sanitise"), exports);
|
|
28
|
+
__exportStar(require("./catalog/lifecycle"), exports);
|
|
29
|
+
__exportStar(require("./catalog/risk"), exports);
|
|
30
|
+
__exportStar(require("./catalog/ingest"), exports);
|
|
31
|
+
__exportStar(require("./catalog/memory-store"), exports);
|
|
32
|
+
__exportStar(require("./catalog/file-store"), exports);
|
|
33
|
+
__exportStar(require("./adapters/raw"), exports);
|
|
34
|
+
var mcp_registry_1 = require("./adapters/mcp-registry");
|
|
35
|
+
Object.defineProperty(exports, "fromServerJson", { enumerable: true, get: function () { return mcp_registry_1.fromServerJson; } });
|
|
36
|
+
var mcp_tools_1 = require("./adapters/mcp-tools");
|
|
37
|
+
Object.defineProperty(exports, "fromMcpToolsList", { enumerable: true, get: function () { return mcp_tools_1.fromMcpToolsList; } });
|
|
38
|
+
Object.defineProperty(exports, "listToolsLive", { enumerable: true, get: function () { return mcp_tools_1.listToolsLive; } });
|
|
39
|
+
var openapi_1 = require("./adapters/openapi");
|
|
40
|
+
Object.defineProperty(exports, "fromOpenApi", { enumerable: true, get: function () { return openapi_1.fromOpenApi; } });
|
|
41
|
+
var pay_skills_1 = require("./adapters/pay-skills");
|
|
42
|
+
Object.defineProperty(exports, "fromPaySkills", { enumerable: true, get: function () { return pay_skills_1.fromPaySkills; } });
|
|
43
|
+
var x402_discovery_1 = require("./adapters/x402-discovery");
|
|
44
|
+
Object.defineProperty(exports, "fromX402Resources", { enumerable: true, get: function () { return x402_discovery_1.fromX402Resources; } });
|
|
45
|
+
var awesome_list_1 = require("./adapters/awesome-list");
|
|
46
|
+
Object.defineProperty(exports, "fromAwesomeMarkdown", { enumerable: true, get: function () { return awesome_list_1.fromAwesomeMarkdown; } });
|
|
47
|
+
__exportStar(require("./classify/taxonomy"), exports);
|
|
48
|
+
var rules_1 = require("./classify/rules");
|
|
49
|
+
Object.defineProperty(exports, "classifyRules", { enumerable: true, get: function () { return rules_1.classifyRules; } });
|
|
50
|
+
var classifier_1 = require("./classify/classifier");
|
|
51
|
+
Object.defineProperty(exports, "classify", { enumerable: true, get: function () { return classifier_1.classify; } });
|
|
52
|
+
var decompose_1 = require("./classify/decompose");
|
|
53
|
+
Object.defineProperty(exports, "decompose", { enumerable: true, get: function () { return decompose_1.decompose; } });
|
|
54
|
+
Object.defineProperty(exports, "topoOrder", { enumerable: true, get: function () { return decompose_1.topoOrder; } });
|
|
55
|
+
var tagger_1 = require("./classify/tagger");
|
|
56
|
+
Object.defineProperty(exports, "tagCapabilities", { enumerable: true, get: function () { return tagger_1.tagCapabilities; } });
|
|
57
|
+
var tokenise_1 = require("./retrieve/tokenise");
|
|
58
|
+
Object.defineProperty(exports, "tokenise", { enumerable: true, get: function () { return tokenise_1.tokenise; } });
|
|
59
|
+
var bm25_1 = require("./retrieve/bm25");
|
|
60
|
+
Object.defineProperty(exports, "Bm25Index", { enumerable: true, get: function () { return bm25_1.Bm25Index; } });
|
|
61
|
+
var vector_1 = require("./retrieve/vector");
|
|
62
|
+
Object.defineProperty(exports, "topK", { enumerable: true, get: function () { return vector_1.topK; } });
|
|
63
|
+
Object.defineProperty(exports, "cosineAll", { enumerable: true, get: function () { return vector_1.cosineAll; } });
|
|
64
|
+
var hash_embed_1 = require("./retrieve/hash-embed");
|
|
65
|
+
Object.defineProperty(exports, "hashEmbed", { enumerable: true, get: function () { return hash_embed_1.hashEmbed; } });
|
|
66
|
+
Object.defineProperty(exports, "hashEmbeddingPort", { enumerable: true, get: function () { return hash_embed_1.hashEmbeddingPort; } });
|
|
67
|
+
Object.defineProperty(exports, "HASH_MODEL", { enumerable: true, get: function () { return hash_embed_1.HASH_MODEL; } });
|
|
68
|
+
var hybrid_1 = require("./retrieve/hybrid");
|
|
69
|
+
Object.defineProperty(exports, "RouterIndex", { enumerable: true, get: function () { return hybrid_1.RouterIndex; } });
|
|
70
|
+
Object.defineProperty(exports, "retrieve", { enumerable: true, get: function () { return hybrid_1.retrieve; } });
|
|
71
|
+
Object.defineProperty(exports, "docText", { enumerable: true, get: function () { return hybrid_1.docText; } });
|
|
72
|
+
Object.defineProperty(exports, "CALIBRATION", { enumerable: true, get: function () { return hybrid_1.CALIBRATION; } });
|
|
73
|
+
var rules_2 = require("./eligibility/rules");
|
|
74
|
+
Object.defineProperty(exports, "firstFailure", { enumerable: true, get: function () { return rules_2.firstFailure; } });
|
|
75
|
+
Object.defineProperty(exports, "authNeeded", { enumerable: true, get: function () { return rules_2.authNeeded; } });
|
|
76
|
+
Object.defineProperty(exports, "RULE_IDS", { enumerable: true, get: function () { return rules_2.RULE_IDS; } });
|
|
77
|
+
Object.defineProperty(exports, "RULE_LABEL", { enumerable: true, get: function () { return rules_2.RULE_LABEL; } });
|
|
78
|
+
var engine_1 = require("./eligibility/engine");
|
|
79
|
+
Object.defineProperty(exports, "filter", { enumerable: true, get: function () { return engine_1.filter; } });
|
|
80
|
+
var confidence_1 = require("./rank/confidence");
|
|
81
|
+
Object.defineProperty(exports, "wilsonLower", { enumerable: true, get: function () { return confidence_1.wilsonLower; } });
|
|
82
|
+
Object.defineProperty(exports, "successScore", { enumerable: true, get: function () { return confidence_1.successScore; } });
|
|
83
|
+
var weights_1 = require("./rank/weights");
|
|
84
|
+
Object.defineProperty(exports, "DEFAULT_WEIGHTS", { enumerable: true, get: function () { return weights_1.DEFAULT_WEIGHTS; } });
|
|
85
|
+
Object.defineProperty(exports, "DEFAULT_FIT", { enumerable: true, get: function () { return weights_1.DEFAULT_FIT; } });
|
|
86
|
+
Object.defineProperty(exports, "RELEVANCE_FLOOR", { enumerable: true, get: function () { return weights_1.RELEVANCE_FLOOR; } });
|
|
87
|
+
Object.defineProperty(exports, "AIAX_READY_WEIGHT", { enumerable: true, get: function () { return weights_1.AIAX_READY_WEIGHT; } });
|
|
88
|
+
Object.defineProperty(exports, "checkWeights", { enumerable: true, get: function () { return weights_1.checkWeights; } });
|
|
89
|
+
var ranker_1 = require("./rank/ranker");
|
|
90
|
+
Object.defineProperty(exports, "rank", { enumerable: true, get: function () { return ranker_1.rank; } });
|
|
91
|
+
var generate_1 = require("./shortlist/generate");
|
|
92
|
+
Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return generate_1.generate; } });
|
|
93
|
+
Object.defineProperty(exports, "MAX_CANDIDATES", { enumerable: true, get: function () { return generate_1.MAX_CANDIDATES; } });
|
|
94
|
+
var router_1 = require("./router");
|
|
95
|
+
Object.defineProperty(exports, "route", { enumerable: true, get: function () { return router_1.route; } });
|
package/dist/money.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integer micro units (1 USDC = 1_000_000). Decimal strings in, bigint inside, decimal strings on disk. There is no
|
|
3
|
+
* floating point path: "0.17" is parsed digit by digit.
|
|
4
|
+
*/
|
|
5
|
+
import type { Money } from './types/tool';
|
|
6
|
+
export declare function toMicro(decimal: string, decimals?: number): bigint;
|
|
7
|
+
export declare function fromMicro(b: bigint, decimals?: number): string;
|
|
8
|
+
/** Parses a micro unit string from disk. */
|
|
9
|
+
export declare function microOf(s: string | undefined | null): bigint | null;
|
|
10
|
+
export declare function fmt(m: Money): string;
|
package/dist/money.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toMicro = toMicro;
|
|
4
|
+
exports.fromMicro = fromMicro;
|
|
5
|
+
exports.microOf = microOf;
|
|
6
|
+
exports.fmt = fmt;
|
|
7
|
+
const DEC_RE = /^(\d+)(?:\.(\d+))?$/;
|
|
8
|
+
function toMicro(decimal, decimals = 6) {
|
|
9
|
+
const s = String(decimal).trim();
|
|
10
|
+
if (s.startsWith('-'))
|
|
11
|
+
throw new Error('negative amounts are refused');
|
|
12
|
+
const m = DEC_RE.exec(s);
|
|
13
|
+
if (!m)
|
|
14
|
+
throw new Error(`not a decimal amount: "${decimal}"`);
|
|
15
|
+
const frac = m[2] || '';
|
|
16
|
+
if (frac.length > decimals && /[1-9]/.test(frac.slice(decimals)))
|
|
17
|
+
throw new Error(`more than ${decimals} decimals: "${decimal}"`);
|
|
18
|
+
const padded = (frac + '0'.repeat(decimals)).slice(0, decimals);
|
|
19
|
+
return BigInt(m[1]) * 10n ** BigInt(decimals) + BigInt(padded || '0');
|
|
20
|
+
}
|
|
21
|
+
function fromMicro(b, decimals = 6) {
|
|
22
|
+
if (b < 0n)
|
|
23
|
+
throw new Error('negative amounts are refused');
|
|
24
|
+
const base = 10n ** BigInt(decimals);
|
|
25
|
+
const whole = b / base;
|
|
26
|
+
const frac = (b % base).toString().padStart(decimals, '0').replace(/0+$/, '');
|
|
27
|
+
return frac ? `${whole}.${frac}` : whole.toString();
|
|
28
|
+
}
|
|
29
|
+
/** Parses a micro unit string from disk. */
|
|
30
|
+
function microOf(s) {
|
|
31
|
+
if (s === undefined || s === null || !/^\d{1,30}$/.test(String(s)))
|
|
32
|
+
return null;
|
|
33
|
+
return BigInt(s);
|
|
34
|
+
}
|
|
35
|
+
function fmt(m) {
|
|
36
|
+
const b = microOf(m.amount_micro);
|
|
37
|
+
if (b === null)
|
|
38
|
+
return `? ${m.currency}`;
|
|
39
|
+
const dec = m.currency === 'KGC' ? 0 : 6;
|
|
40
|
+
return `${dec ? fromMicro(b) : b.toString()} ${m.currency}${m.network ? ` on ${m.network}` : ''}`;
|
|
41
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Success scores that know how sure they are (plan.md S3, threat T10). The Wilson lower bound (z 1.64) of verified
|
|
3
|
+
* successes, with a Beta prior by origin and trust: Kognai verified (4, 1), reviewed outside (2, 2), anything else
|
|
4
|
+
* (1, 1). Execution success (the call returned) counts only when nothing was verified, at half weight, with its sample
|
|
5
|
+
* capped and shrunk by the number of distinct principals, so a tool cannot buy a score with self reports.
|
|
6
|
+
*/
|
|
7
|
+
import type { SuccessStats, ToolRecord } from '../types/tool';
|
|
8
|
+
export declare const Z = 1.64;
|
|
9
|
+
export declare function wilsonLower(ok: number, n: number, z?: number): number;
|
|
10
|
+
export declare function priorOf(rec: Pick<ToolRecord, 'origin' | 'trust_state'>): [number, number];
|
|
11
|
+
export declare const EXEC_CAP = 20;
|
|
12
|
+
export declare const PRINCIPALS_FULL = 10;
|
|
13
|
+
export declare function successScore(rec: Pick<ToolRecord, 'origin' | 'trust_state'>, s: SuccessStats): number;
|
|
14
|
+
/** How much the evidence says about the tool, 0..1: trust plus sample size. */
|
|
15
|
+
export declare function evidenceConfidence(rec: Pick<ToolRecord, 'trust_state' | 'origin'>, s: SuccessStats): number;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRINCIPALS_FULL = exports.EXEC_CAP = exports.Z = void 0;
|
|
4
|
+
exports.wilsonLower = wilsonLower;
|
|
5
|
+
exports.priorOf = priorOf;
|
|
6
|
+
exports.successScore = successScore;
|
|
7
|
+
exports.evidenceConfidence = evidenceConfidence;
|
|
8
|
+
exports.Z = 1.64;
|
|
9
|
+
function wilsonLower(ok, n, z = exports.Z) {
|
|
10
|
+
if (n <= 0)
|
|
11
|
+
return 0;
|
|
12
|
+
const p = Math.min(1, Math.max(0, ok / n));
|
|
13
|
+
const z2 = z * z;
|
|
14
|
+
const den = 1 + z2 / n;
|
|
15
|
+
const centre = p + z2 / (2 * n);
|
|
16
|
+
const margin = z * Math.sqrt((p * (1 - p) + z2 / (4 * n)) / n);
|
|
17
|
+
return Math.max(0, (centre - margin) / den);
|
|
18
|
+
}
|
|
19
|
+
function priorOf(rec) {
|
|
20
|
+
if (rec.origin === 'kognai' && rec.trust_state === 'verified')
|
|
21
|
+
return [4, 1];
|
|
22
|
+
if (rec.trust_state === 'reviewed' || rec.trust_state === 'verified')
|
|
23
|
+
return [2, 2];
|
|
24
|
+
return [1, 1];
|
|
25
|
+
}
|
|
26
|
+
exports.EXEC_CAP = 20;
|
|
27
|
+
exports.PRINCIPALS_FULL = 10;
|
|
28
|
+
function successScore(rec, s) {
|
|
29
|
+
const [a, b] = priorOf(rec);
|
|
30
|
+
if (s.verified_n > 0) {
|
|
31
|
+
const shrink = Math.min(1, Math.max(1, s.distinct_principals) / exports.PRINCIPALS_FULL);
|
|
32
|
+
const n = s.verified_n * shrink;
|
|
33
|
+
const ok = s.verified_ok * shrink;
|
|
34
|
+
return wilsonLower(ok + a, n + a + b);
|
|
35
|
+
}
|
|
36
|
+
if (s.exec_n > 0) {
|
|
37
|
+
const shrink = Math.min(1, Math.max(1, s.distinct_principals) / exports.PRINCIPALS_FULL);
|
|
38
|
+
const n = Math.min(exports.EXEC_CAP, s.exec_n) * shrink;
|
|
39
|
+
const ok = n * (s.exec_ok / s.exec_n);
|
|
40
|
+
return 0.5 * wilsonLower(ok + a, n + a + b);
|
|
41
|
+
}
|
|
42
|
+
return wilsonLower(a, a + b);
|
|
43
|
+
}
|
|
44
|
+
/** How much the evidence says about the tool, 0..1: trust plus sample size. */
|
|
45
|
+
function evidenceConfidence(rec, s) {
|
|
46
|
+
const base = rec.trust_state === 'verified' ? 0.7 : rec.trust_state === 'reviewed' ? 0.55 : 0.3;
|
|
47
|
+
return Math.min(1, base + 0.3 * (1 - 1 / (1 + s.verified_n / 10)));
|
|
48
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Retrieved } from '../retrieve/hybrid';
|
|
2
|
+
import type { ScorePart } from '../types/route';
|
|
3
|
+
import type { EvidenceStore, SkillIndex, SkillRef } from '../types/ports';
|
|
4
|
+
import type { SuccessStats } from '../types/tool';
|
|
5
|
+
import { type FitWeights, type Weights } from './weights';
|
|
6
|
+
export interface RankOpts {
|
|
7
|
+
weights?: Weights;
|
|
8
|
+
fitWeights?: FitWeights;
|
|
9
|
+
use?: {
|
|
10
|
+
capability: boolean;
|
|
11
|
+
vector: boolean;
|
|
12
|
+
lexical: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** 'full' = every part; 'fit' = fit only (the hybrid retrieval baseline of the benchmark). */
|
|
15
|
+
scoring?: 'full' | 'fit';
|
|
16
|
+
seed?: string;
|
|
17
|
+
exploration?: boolean;
|
|
18
|
+
evidence?: EvidenceStore | null;
|
|
19
|
+
skills?: SkillIndex | null;
|
|
20
|
+
/** The AIAX ready bonus (weights.ts AIAX_READY_WEIGHT by default); 0 turns it off. */
|
|
21
|
+
aiaxWeight?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface Ranked {
|
|
24
|
+
r: Retrieved;
|
|
25
|
+
fit: number;
|
|
26
|
+
score: number;
|
|
27
|
+
parts: Record<ScorePart, number>;
|
|
28
|
+
reasons: string[];
|
|
29
|
+
confidence: number;
|
|
30
|
+
skills: SkillRef[];
|
|
31
|
+
stats: SuccessStats;
|
|
32
|
+
cold: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function rank(cands: Retrieved[], o?: RankOpts): Ranked[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rank = rank;
|
|
4
|
+
/**
|
|
5
|
+
* The ranker (plan.md S3): score = weighted parts + exploration. Reasons are written from the parts by this code,
|
|
6
|
+
* never copied from provider text (T1). Deterministic for a given seed: exploration is a hash of the route id and the
|
|
7
|
+
* tool id, off unless asked for.
|
|
8
|
+
*/
|
|
9
|
+
const node_crypto_1 = require("node:crypto");
|
|
10
|
+
const rules_1 = require("../eligibility/rules");
|
|
11
|
+
const confidence_1 = require("./confidence");
|
|
12
|
+
const weights_1 = require("./weights");
|
|
13
|
+
const unitHash = (s) => (0, node_crypto_1.createHash)('sha256').update(s).digest().readUInt32BE(0) / 0xffffffff;
|
|
14
|
+
function rank(cands, o = {}) {
|
|
15
|
+
const w = o.weights || weights_1.DEFAULT_WEIGHTS;
|
|
16
|
+
const aiaxW = typeof o.aiaxWeight === 'number' && Number.isFinite(o.aiaxWeight) && o.aiaxWeight >= 0 ? o.aiaxWeight : weights_1.AIAX_READY_WEIGHT;
|
|
17
|
+
const use = o.use || { capability: true, vector: true, lexical: true };
|
|
18
|
+
// cost and latency are normalised inside the node's candidate set
|
|
19
|
+
const prices = cands.map((c) => (0, rules_1.priceMicroUsd)(c.item.rec));
|
|
20
|
+
const known = prices.filter((p) => p !== null && p > 0n).map((p) => Math.log10(Number(p) + 1));
|
|
21
|
+
const pMin = known.length ? Math.min(...known) : 0;
|
|
22
|
+
const pMax = known.length ? Math.max(...known) : 0;
|
|
23
|
+
const lats = cands.map((c) => c.item.rec.latency.p50_ms).filter((x) => typeof x === 'number');
|
|
24
|
+
const lMin = lats.length ? Math.min(...lats) : 0;
|
|
25
|
+
const lMax = lats.length ? Math.max(...lats) : 0;
|
|
26
|
+
const out = cands.map((c, i) => {
|
|
27
|
+
const rec = c.item.rec;
|
|
28
|
+
const fit = (0, weights_1.fitOf)(c.signals, use, o.fitWeights || weights_1.DEFAULT_FIT);
|
|
29
|
+
const stats = o.evidence?.stats(rec.id) || rec.success;
|
|
30
|
+
const success = (0, confidence_1.successScore)(rec, stats);
|
|
31
|
+
const p = prices[i];
|
|
32
|
+
const cost = p === null ? 0.5 : p === 0n ? 1 : (pMax > pMin ? 1 - (Math.log10(Number(p) + 1) - pMin) / (pMax - pMin) : 0.8);
|
|
33
|
+
const l = rec.latency.p50_ms;
|
|
34
|
+
const latency = typeof l !== 'number' ? 0.5 : lMax > lMin ? 1 - (l - lMin) / (lMax - lMin) : 0.8;
|
|
35
|
+
const skills = (o.skills?.forTool(rec.id, c.item.op.op) || []).filter((s) => s.uplift > 0).sort((a, b) => b.uplift - a.uplift);
|
|
36
|
+
const skillPart = skills.length ? Math.min(1, skills[0].uplift) : 0;
|
|
37
|
+
const cold = stats.verified_n < 5;
|
|
38
|
+
const exploration = o.exploration && cold && fit >= 0.7 ? weights_1.EXPLORATION_MAX * unitHash(`${o.seed || ''}|${rec.id}`) : 0;
|
|
39
|
+
const aiax = rec.aiax_ready === true ? aiaxW : 0;
|
|
40
|
+
const parts = { fit, schema: c.signals.schema, success, cost, latency, skills: skillPart, exploration, aiax };
|
|
41
|
+
const score = o.scoring === 'fit' ? fit
|
|
42
|
+
: w.fit * fit + w.schema * c.signals.schema + w.success * success + w.cost * cost + w.latency * latency + w.skills * skillPart + exploration + aiax;
|
|
43
|
+
return { r: c, fit, score, parts, reasons: reasonsOf(c, parts, rec.trust_state, rec.origin, p, cold, skills), confidence: Math.min(fit, (0, confidence_1.evidenceConfidence)(rec, stats)), skills, stats, cold };
|
|
44
|
+
});
|
|
45
|
+
return out.sort((a, b) => b.score - a.score || (a.r.item.key < b.r.item.key ? -1 : 1));
|
|
46
|
+
}
|
|
47
|
+
function reasonsOf(c, parts, trust, origin, price, cold, skills) {
|
|
48
|
+
const r = [];
|
|
49
|
+
if (c.signals.capability === 1)
|
|
50
|
+
r.push('capability matches the step');
|
|
51
|
+
else if (c.signals.capability === 0.5)
|
|
52
|
+
r.push('related capability (same family)');
|
|
53
|
+
if (c.signals.lexical >= 0.5)
|
|
54
|
+
r.push('strong match on the request words');
|
|
55
|
+
else if (c.signals.lexical > 0)
|
|
56
|
+
r.push('partial match on the request words');
|
|
57
|
+
if (c.signals.cosine !== null)
|
|
58
|
+
r.push(`semantic similarity ${c.signals.cosine.toFixed(2)}`);
|
|
59
|
+
if (c.signals.schema === 0.3)
|
|
60
|
+
r.push('input schema unknown');
|
|
61
|
+
else if (c.signals.schema >= 0.8)
|
|
62
|
+
r.push('the request supplies the required inputs');
|
|
63
|
+
else
|
|
64
|
+
r.push('some required inputs are missing');
|
|
65
|
+
if (trust === 'verified' && origin === 'kognai')
|
|
66
|
+
r.push('Kognai tool, verified in the repository');
|
|
67
|
+
else if (trust === 'verified' || trust === 'reviewed')
|
|
68
|
+
r.push(`outside tool, ${trust} by the CTO desk`);
|
|
69
|
+
else if (trust === 'quarantined')
|
|
70
|
+
r.push('quarantined: listed for discovery, not executable');
|
|
71
|
+
r.push(price === null ? 'price unknown' : price === 0n ? 'free' : 'priced per call');
|
|
72
|
+
if (cold)
|
|
73
|
+
r.push('no verified runs yet (cold start)');
|
|
74
|
+
if (skills.length)
|
|
75
|
+
r.push(`a skill applies (${skills[0].evaluated ? 'evaluated' : 'unevaluated'} uplift ${skills[0].uplift.toFixed(2)})`);
|
|
76
|
+
if (parts.exploration > 0)
|
|
77
|
+
r.push('exploration bonus for a new tool');
|
|
78
|
+
if (parts.aiax > 0)
|
|
79
|
+
r.push('provider publishes a valid AIAX manifest');
|
|
80
|
+
return r;
|
|
81
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ranking weights (plan.md S3, the directive's split): fit 30, schema 20, success 20, cost 10, latency 10, skills 10,
|
|
3
|
+
* plus up to 0.03 exploration. fit = 0.45 capability + 0.35 vector + 0.20 lexical. Configurable; validated to sum to 1.
|
|
4
|
+
*/
|
|
5
|
+
export interface Weights {
|
|
6
|
+
fit: number;
|
|
7
|
+
schema: number;
|
|
8
|
+
success: number;
|
|
9
|
+
cost: number;
|
|
10
|
+
latency: number;
|
|
11
|
+
skills: number;
|
|
12
|
+
}
|
|
13
|
+
export interface FitWeights {
|
|
14
|
+
capability: number;
|
|
15
|
+
vector: number;
|
|
16
|
+
lexical: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const DEFAULT_WEIGHTS: Weights;
|
|
19
|
+
export declare const DEFAULT_FIT: FitWeights;
|
|
20
|
+
export declare const EXPLORATION_MAX = 0.03;
|
|
21
|
+
/**
|
|
22
|
+
* AIAX ready (2026-10-12, AIAX rollout): a tool whose provider serves a valid AIAX manifest (/aiax/manifest.json,
|
|
23
|
+
* spec github.com/Godman-s/aiax) gets this flat bonus on top of the weighted parts, like exploration. A tie breaker
|
|
24
|
+
* only: at 0.02 it is smaller than a 0.07 gap in fit (0.30 x 0.07), so it never beats a clearly better match.
|
|
25
|
+
* Configurable per call (RankOpts.aiaxWeight, RouterConfig.aiaxWeight); 0 turns it off.
|
|
26
|
+
*/
|
|
27
|
+
export declare const AIAX_READY_WEIGHT = 0.02;
|
|
28
|
+
/** A candidate whose fit is under this is not shown (fewer than 10 is normal). */
|
|
29
|
+
export declare const RELEVANCE_FLOOR = 0.35;
|
|
30
|
+
export declare function checkWeights(w: Weights, f?: FitWeights): void;
|
|
31
|
+
/** Fit from the signals in use: weights of signals that are off are spread over the others. */
|
|
32
|
+
export declare function fitOf(s: {
|
|
33
|
+
capability: number;
|
|
34
|
+
vector: number;
|
|
35
|
+
lexical: number;
|
|
36
|
+
}, use: {
|
|
37
|
+
capability: boolean;
|
|
38
|
+
vector: boolean;
|
|
39
|
+
lexical: boolean;
|
|
40
|
+
}, f?: FitWeights): number;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RELEVANCE_FLOOR = exports.AIAX_READY_WEIGHT = exports.EXPLORATION_MAX = exports.DEFAULT_FIT = exports.DEFAULT_WEIGHTS = void 0;
|
|
4
|
+
exports.checkWeights = checkWeights;
|
|
5
|
+
exports.fitOf = fitOf;
|
|
6
|
+
exports.DEFAULT_WEIGHTS = { fit: 0.30, schema: 0.20, success: 0.20, cost: 0.10, latency: 0.10, skills: 0.10 };
|
|
7
|
+
exports.DEFAULT_FIT = { capability: 0.45, vector: 0.35, lexical: 0.20 };
|
|
8
|
+
exports.EXPLORATION_MAX = 0.03;
|
|
9
|
+
/**
|
|
10
|
+
* AIAX ready (2026-10-12, AIAX rollout): a tool whose provider serves a valid AIAX manifest (/aiax/manifest.json,
|
|
11
|
+
* spec github.com/Godman-s/aiax) gets this flat bonus on top of the weighted parts, like exploration. A tie breaker
|
|
12
|
+
* only: at 0.02 it is smaller than a 0.07 gap in fit (0.30 x 0.07), so it never beats a clearly better match.
|
|
13
|
+
* Configurable per call (RankOpts.aiaxWeight, RouterConfig.aiaxWeight); 0 turns it off.
|
|
14
|
+
*/
|
|
15
|
+
exports.AIAX_READY_WEIGHT = 0.02;
|
|
16
|
+
/** A candidate whose fit is under this is not shown (fewer than 10 is normal). */
|
|
17
|
+
exports.RELEVANCE_FLOOR = 0.35;
|
|
18
|
+
function checkWeights(w, f = exports.DEFAULT_FIT) {
|
|
19
|
+
const s = w.fit + w.schema + w.success + w.cost + w.latency + w.skills;
|
|
20
|
+
if (Math.abs(s - 1) > 1e-9)
|
|
21
|
+
throw new Error(`ranking weights sum to ${s}, not 1`);
|
|
22
|
+
const t = f.capability + f.vector + f.lexical;
|
|
23
|
+
if (Math.abs(t - 1) > 1e-9)
|
|
24
|
+
throw new Error(`fit weights sum to ${t}, not 1`);
|
|
25
|
+
for (const v of [...Object.values(w), ...Object.values(f)])
|
|
26
|
+
if (v < 0)
|
|
27
|
+
throw new Error('negative weight');
|
|
28
|
+
}
|
|
29
|
+
/** Fit from the signals in use: weights of signals that are off are spread over the others. */
|
|
30
|
+
function fitOf(s, use, f = exports.DEFAULT_FIT) {
|
|
31
|
+
let num = 0;
|
|
32
|
+
let den = 0;
|
|
33
|
+
if (use.capability) {
|
|
34
|
+
num += f.capability * s.capability;
|
|
35
|
+
den += f.capability;
|
|
36
|
+
}
|
|
37
|
+
if (use.vector) {
|
|
38
|
+
num += f.vector * s.vector;
|
|
39
|
+
den += f.vector;
|
|
40
|
+
}
|
|
41
|
+
if (use.lexical) {
|
|
42
|
+
num += f.lexical * s.lexical;
|
|
43
|
+
den += f.lexical;
|
|
44
|
+
}
|
|
45
|
+
return den ? num / den : 0;
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare class Bm25Index {
|
|
2
|
+
readonly k1: number;
|
|
3
|
+
readonly b: number;
|
|
4
|
+
private postings;
|
|
5
|
+
private lengths;
|
|
6
|
+
private avgdl;
|
|
7
|
+
readonly keys: string[];
|
|
8
|
+
constructor(docs: Array<{
|
|
9
|
+
key: string;
|
|
10
|
+
text: string;
|
|
11
|
+
}>, k1?: number, b?: number);
|
|
12
|
+
get size(): number;
|
|
13
|
+
idf(term: string): number;
|
|
14
|
+
/** Scores by document index, for the given query tokens (repeats weigh more). */
|
|
15
|
+
scoreAll(queryTokens: string[]): Map<number, number>;
|
|
16
|
+
search(q: string | string[], k: number): Array<{
|
|
17
|
+
key: string;
|
|
18
|
+
index: number;
|
|
19
|
+
score: number;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Bm25Index = void 0;
|
|
4
|
+
/** BM25 lexical index (k1 1.2, b 0.75) over tokenised documents, built in memory at load time (ADR-003 rule 4). */
|
|
5
|
+
const tokenise_1 = require("./tokenise");
|
|
6
|
+
class Bm25Index {
|
|
7
|
+
k1;
|
|
8
|
+
b;
|
|
9
|
+
postings = new Map();
|
|
10
|
+
lengths;
|
|
11
|
+
avgdl;
|
|
12
|
+
keys;
|
|
13
|
+
constructor(docs, k1 = 1.2, b = 0.75) {
|
|
14
|
+
this.k1 = k1;
|
|
15
|
+
this.b = b;
|
|
16
|
+
this.keys = docs.map((d) => d.key);
|
|
17
|
+
this.lengths = new Float64Array(docs.length);
|
|
18
|
+
let total = 0;
|
|
19
|
+
docs.forEach((d, i) => {
|
|
20
|
+
const toks = (0, tokenise_1.tokenise)(d.text);
|
|
21
|
+
this.lengths[i] = toks.length;
|
|
22
|
+
total += toks.length;
|
|
23
|
+
const tf = new Map();
|
|
24
|
+
for (const t of toks)
|
|
25
|
+
tf.set(t, (tf.get(t) || 0) + 1);
|
|
26
|
+
for (const [t, n] of tf)
|
|
27
|
+
(this.postings.get(t) || this.postings.set(t, []).get(t)).push([i, n]);
|
|
28
|
+
});
|
|
29
|
+
this.avgdl = docs.length ? total / docs.length : 1;
|
|
30
|
+
}
|
|
31
|
+
get size() { return this.keys.length; }
|
|
32
|
+
idf(term) {
|
|
33
|
+
const df = this.postings.get(term)?.length || 0;
|
|
34
|
+
return Math.log(1 + (this.keys.length - df + 0.5) / (df + 0.5));
|
|
35
|
+
}
|
|
36
|
+
/** Scores by document index, for the given query tokens (repeats weigh more). */
|
|
37
|
+
scoreAll(queryTokens) {
|
|
38
|
+
const scores = new Map();
|
|
39
|
+
const qtf = new Map();
|
|
40
|
+
for (const t of queryTokens)
|
|
41
|
+
qtf.set(t, (qtf.get(t) || 0) + 1);
|
|
42
|
+
for (const [t, qn] of qtf) {
|
|
43
|
+
const post = this.postings.get(t);
|
|
44
|
+
if (!post)
|
|
45
|
+
continue;
|
|
46
|
+
const idf = this.idf(t);
|
|
47
|
+
for (const [i, tf] of post) {
|
|
48
|
+
const s = idf * (tf * (this.k1 + 1)) / (tf + this.k1 * (1 - this.b + this.b * this.lengths[i] / this.avgdl));
|
|
49
|
+
scores.set(i, (scores.get(i) || 0) + s * qn);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return scores;
|
|
53
|
+
}
|
|
54
|
+
search(q, k) {
|
|
55
|
+
const toks = Array.isArray(q) ? q : (0, tokenise_1.tokenise)(q);
|
|
56
|
+
return [...this.scoreAll(toks)].sort((a, b) => b[1] - a[1] || a[0] - b[0]).slice(0, k).map(([i, s]) => ({ key: this.keys[i], index: i, score: s }));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.Bm25Index = Bm25Index;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function capabilityScore(nodeCap: string, opCaps: string[]): number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.capabilityScore = capabilityScore;
|
|
4
|
+
/** Capability match: the classifier's capability on the operation = 1, a sibling in the same family = 0.5, else 0. */
|
|
5
|
+
const taxonomy_1 = require("../classify/taxonomy");
|
|
6
|
+
function capabilityScore(nodeCap, opCaps) {
|
|
7
|
+
if (opCaps.includes(nodeCap))
|
|
8
|
+
return 1;
|
|
9
|
+
const fam = (0, taxonomy_1.familyOf)(nodeCap);
|
|
10
|
+
if (fam && opCaps.some((c) => (0, taxonomy_1.familyOf)(c) === fam))
|
|
11
|
+
return 0.5;
|
|
12
|
+
return 0;
|
|
13
|
+
}
|