@knowledgine/core 0.1.0 → 0.2.1
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/config/config-loader.d.ts +22 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +73 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/embedding/model-downloader.d.ts +34 -0
- package/dist/embedding/model-downloader.d.ts.map +1 -0
- package/dist/embedding/model-downloader.js +168 -0
- package/dist/embedding/model-downloader.js.map +1 -0
- package/dist/embedding/onnx-embedding-provider.js +2 -2
- package/dist/embedding/onnx-embedding-provider.js.map +1 -1
- package/dist/feedback/feedback-learner.d.ts +29 -0
- package/dist/feedback/feedback-learner.d.ts.map +1 -0
- package/dist/feedback/feedback-learner.js +88 -0
- package/dist/feedback/feedback-learner.js.map +1 -0
- package/dist/feedback/feedback-repository.d.ts +41 -0
- package/dist/feedback/feedback-repository.d.ts.map +1 -0
- package/dist/feedback/feedback-repository.js +90 -0
- package/dist/feedback/feedback-repository.js.map +1 -0
- package/dist/feedback/index.d.ts +5 -0
- package/dist/feedback/index.d.ts.map +1 -0
- package/dist/feedback/index.js +3 -0
- package/dist/feedback/index.js.map +1 -0
- package/dist/graph/entity-extractor.d.ts +8 -2
- package/dist/graph/entity-extractor.d.ts.map +1 -1
- package/dist/graph/entity-extractor.js +94 -12
- package/dist/graph/entity-extractor.js.map +1 -1
- package/dist/graph/graph-repository.d.ts +9 -0
- package/dist/graph/graph-repository.d.ts.map +1 -1
- package/dist/graph/graph-repository.js +49 -14
- package/dist/graph/graph-repository.js.map +1 -1
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -4
- package/dist/index.js.map +1 -1
- package/dist/provenance/provenance-repository.d.ts +47 -0
- package/dist/provenance/provenance-repository.d.ts.map +1 -0
- package/dist/provenance/provenance-repository.js +121 -0
- package/dist/provenance/provenance-repository.js.map +1 -0
- package/dist/search/hybrid-searcher.js +2 -2
- package/dist/search/hybrid-searcher.js.map +1 -1
- package/dist/search/knowledge-searcher.d.ts.map +1 -1
- package/dist/search/knowledge-searcher.js +9 -1
- package/dist/search/knowledge-searcher.js.map +1 -1
- package/dist/search/semantic-searcher.d.ts.map +1 -1
- package/dist/search/semantic-searcher.js +3 -2
- package/dist/search/semantic-searcher.js.map +1 -1
- package/dist/services/knowledge-service.d.ts +128 -0
- package/dist/services/knowledge-service.d.ts.map +1 -0
- package/dist/services/knowledge-service.js +170 -0
- package/dist/services/knowledge-service.js.map +1 -0
- package/dist/storage/database.d.ts +5 -0
- package/dist/storage/database.d.ts.map +1 -1
- package/dist/storage/database.js +34 -4
- package/dist/storage/database.js.map +1 -1
- package/dist/storage/knowledge-repository.d.ts +17 -0
- package/dist/storage/knowledge-repository.d.ts.map +1 -1
- package/dist/storage/knowledge-repository.js +47 -7
- package/dist/storage/knowledge-repository.js.map +1 -1
- package/dist/storage/migrations/005a_events_layer.d.ts +3 -0
- package/dist/storage/migrations/005a_events_layer.d.ts.map +1 -0
- package/dist/storage/migrations/005a_events_layer.js +55 -0
- package/dist/storage/migrations/005a_events_layer.js.map +1 -0
- package/dist/storage/migrations/005b_bitemporal.d.ts +3 -0
- package/dist/storage/migrations/005b_bitemporal.d.ts.map +1 -0
- package/dist/storage/migrations/005b_bitemporal.js +53 -0
- package/dist/storage/migrations/005b_bitemporal.js.map +1 -0
- package/dist/storage/migrations/005c_provenance.d.ts +3 -0
- package/dist/storage/migrations/005c_provenance.d.ts.map +1 -0
- package/dist/storage/migrations/005c_provenance.js +56 -0
- package/dist/storage/migrations/005c_provenance.js.map +1 -0
- package/dist/storage/migrations/006_extraction_feedback.d.ts +3 -0
- package/dist/storage/migrations/006_extraction_feedback.d.ts.map +1 -0
- package/dist/storage/migrations/006_extraction_feedback.js +30 -0
- package/dist/storage/migrations/006_extraction_feedback.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { KnowledgineConfig } from "../config.js";
|
|
2
|
+
export interface RcConfig {
|
|
3
|
+
semantic?: boolean;
|
|
4
|
+
defaultPath?: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Load knowledgine configuration from RC file and environment variables.
|
|
9
|
+
* Priority: env var > RC file > defaults (embedding.enabled = false)
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadConfig(rootPath: string): KnowledgineConfig;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the default root path.
|
|
14
|
+
* Priority: cliPath > KNOWLEDGINE_PATH env > cwd/.knowledginerc.json defaultPath > cwd
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveDefaultPath(cliPath?: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Write a .knowledginerc.json config file to the specified directory.
|
|
19
|
+
* Merges with existing config if present.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeRcConfig(dirPath: string, config: RcConfig): void;
|
|
22
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/config/config-loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAgB9D;AAqBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAW3D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAYrE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFileSync, existsSync, writeFileSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { defineConfig } from "../config.js";
|
|
5
|
+
/**
|
|
6
|
+
* Load knowledgine configuration from RC file and environment variables.
|
|
7
|
+
* Priority: env var > RC file > defaults (embedding.enabled = false)
|
|
8
|
+
*/
|
|
9
|
+
export function loadConfig(rootPath) {
|
|
10
|
+
const rcConfig = loadRcFile(rootPath);
|
|
11
|
+
// Environment variable override
|
|
12
|
+
const envSemantic = process.env["KNOWLEDGINE_SEMANTIC"];
|
|
13
|
+
const semanticEnabled = envSemantic === "true" || envSemantic === "1" || rcConfig?.semantic === true;
|
|
14
|
+
return defineConfig({
|
|
15
|
+
rootPath,
|
|
16
|
+
embedding: {
|
|
17
|
+
modelName: "all-MiniLM-L6-v2",
|
|
18
|
+
dimensions: 384,
|
|
19
|
+
enabled: semanticEnabled,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function loadRcFile(rootPath) {
|
|
24
|
+
const jsonPath = resolve(rootPath, ".knowledginerc.json");
|
|
25
|
+
const ymlPath = resolve(rootPath, ".knowledginerc.yml");
|
|
26
|
+
try {
|
|
27
|
+
if (existsSync(jsonPath)) {
|
|
28
|
+
return JSON.parse(readFileSync(jsonPath, "utf-8"));
|
|
29
|
+
}
|
|
30
|
+
if (existsSync(ymlPath)) {
|
|
31
|
+
return parseYaml(readFileSync(ymlPath, "utf-8"));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error(`Warning: Failed to parse config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the default root path.
|
|
41
|
+
* Priority: cliPath > KNOWLEDGINE_PATH env > cwd/.knowledginerc.json defaultPath > cwd
|
|
42
|
+
*/
|
|
43
|
+
export function resolveDefaultPath(cliPath) {
|
|
44
|
+
if (cliPath)
|
|
45
|
+
return resolve(cliPath);
|
|
46
|
+
const envPath = process.env["KNOWLEDGINE_PATH"];
|
|
47
|
+
if (envPath)
|
|
48
|
+
return resolve(envPath);
|
|
49
|
+
// Read from cwd's .knowledginerc.json (NOT rootPath's — avoids circular dependency)
|
|
50
|
+
const rcConfig = loadRcFile(process.cwd());
|
|
51
|
+
if (rcConfig?.defaultPath)
|
|
52
|
+
return resolve(rcConfig.defaultPath);
|
|
53
|
+
return resolve(process.cwd());
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Write a .knowledginerc.json config file to the specified directory.
|
|
57
|
+
* Merges with existing config if present.
|
|
58
|
+
*/
|
|
59
|
+
export function writeRcConfig(dirPath, config) {
|
|
60
|
+
const rcPath = resolve(dirPath, ".knowledginerc.json");
|
|
61
|
+
let existing = {};
|
|
62
|
+
try {
|
|
63
|
+
if (existsSync(rcPath)) {
|
|
64
|
+
existing = JSON.parse(readFileSync(rcPath, "utf-8"));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// If existing file is invalid, overwrite it
|
|
69
|
+
}
|
|
70
|
+
const merged = { ...existing, ...config };
|
|
71
|
+
writeFileSync(rcPath, JSON.stringify(merged, null, 2) + "\n");
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/config/config-loader.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAiC/B,MAAM,cAAc,GAAsB;IACxC,QAAQ,EAAE,GAAG;IACb,MAAM,EAAE,EAAE;IACV,QAAQ,EAAE;QACR,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC;KACrD;IACD,WAAW,EAAE;QACX,cAAc,EAAE,EAAE;KACnB;IACD,SAAS,EAAE;QACT,SAAS,EAAE,kBAAkB;QAC7B,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAiC/B,MAAM,cAAc,GAAsB;IACxC,QAAQ,EAAE,GAAG;IACb,MAAM,EAAE,EAAE;IACV,QAAQ,EAAE;QACR,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC;KACrD;IACD,WAAW,EAAE;QACX,cAAc,EAAE,EAAE;KACnB;IACD,SAAS,EAAE;QACT,SAAS,EAAE,kBAAkB;QAC7B,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,KAAK;KACf;IACD,MAAM,EAAE;QACN,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,GAAG;KACjB;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,UAAsC,EAAE;IACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAAC;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;IAEnF,OAAO;QACL,GAAG,cAAc;QACjB,GAAG,OAAO;QACV,QAAQ;QACR,MAAM;QACN,QAAQ,EAAE;YACR,GAAG,cAAc,CAAC,QAAQ;YAC1B,GAAG,OAAO,CAAC,QAAQ;SACpB;QACD,WAAW,EAAE;YACX,GAAG,cAAc,CAAC,WAAW;YAC7B,GAAG,OAAO,CAAC,WAAW;SACvB;QACD,SAAS,EAAE;YACT,GAAG,cAAc,CAAC,SAAS;YAC3B,GAAG,OAAO,CAAC,SAAS;SACrB;QACD,MAAM,EAAE;YACN,GAAG,cAAc,CAAC,MAAM;YACxB,GAAG,OAAO,CAAC,MAAM;SAClB;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding model downloader.
|
|
3
|
+
* Downloads all-MiniLM-L6-v2 ONNX model files from HuggingFace.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Atomic download (.tmp + rename)
|
|
7
|
+
* - Redirect limit (max 5)
|
|
8
|
+
* - Timeout (5 minutes)
|
|
9
|
+
* - Progress callback
|
|
10
|
+
* - Skip existing files (0-byte treated as corrupt)
|
|
11
|
+
* - SIGINT cleanup
|
|
12
|
+
*/
|
|
13
|
+
import type { ModelManager } from "./model-manager.js";
|
|
14
|
+
export interface DownloadProgress {
|
|
15
|
+
file: string;
|
|
16
|
+
downloaded: number;
|
|
17
|
+
total: number | null;
|
|
18
|
+
}
|
|
19
|
+
export interface DownloadOptions {
|
|
20
|
+
onProgress?: (progress: DownloadProgress) => void;
|
|
21
|
+
onFileComplete?: (file: string) => void;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
maxRedirects?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ModelFile {
|
|
26
|
+
url: string;
|
|
27
|
+
dest: string;
|
|
28
|
+
}
|
|
29
|
+
export declare const MODEL_FILES: ModelFile[];
|
|
30
|
+
export declare function downloadModel(modelManager: ModelManager, options?: DownloadOptions): Promise<{
|
|
31
|
+
downloaded: string[];
|
|
32
|
+
skipped: string[];
|
|
33
|
+
}>;
|
|
34
|
+
//# sourceMappingURL=model-downloader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-downloader.d.ts","sourceRoot":"","sources":["../../src/embedding/model-downloader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAkBD,eAAO,MAAM,WAAW,EAAE,SAAS,EAalC,CAAC;AAgIF,wBAAsB,aAAa,CACjC,YAAY,EAAE,YAAY,EAC1B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqBtD"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding model downloader.
|
|
3
|
+
* Downloads all-MiniLM-L6-v2 ONNX model files from HuggingFace.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Atomic download (.tmp + rename)
|
|
7
|
+
* - Redirect limit (max 5)
|
|
8
|
+
* - Timeout (5 minutes)
|
|
9
|
+
* - Progress callback
|
|
10
|
+
* - Skip existing files (0-byte treated as corrupt)
|
|
11
|
+
* - SIGINT cleanup
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, renameSync, unlinkSync, existsSync, statSync, createWriteStream } from "fs";
|
|
14
|
+
import { get as httpsGet } from "https";
|
|
15
|
+
import { get as httpGet } from "http";
|
|
16
|
+
import { pipeline } from "stream/promises";
|
|
17
|
+
const HF_BASE = "https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main";
|
|
18
|
+
/**
|
|
19
|
+
* Select the best quantized ONNX model for the current platform.
|
|
20
|
+
* HuggingFace removed the generic `model_quantized.onnx` and now ships
|
|
21
|
+
* architecture-specific variants.
|
|
22
|
+
*/
|
|
23
|
+
function selectOnnxModel() {
|
|
24
|
+
const arch = process.arch; // "arm64" | "x64" | ...
|
|
25
|
+
if (arch === "arm64") {
|
|
26
|
+
return `${HF_BASE}/onnx/model_qint8_arm64.onnx`;
|
|
27
|
+
}
|
|
28
|
+
// x64: prefer AVX2 quantized (widely supported on modern x86_64)
|
|
29
|
+
return `${HF_BASE}/onnx/model_quint8_avx2.onnx`;
|
|
30
|
+
}
|
|
31
|
+
export const MODEL_FILES = [
|
|
32
|
+
{
|
|
33
|
+
url: `${HF_BASE}/tokenizer.json`,
|
|
34
|
+
dest: "tokenizer.json",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
url: `${HF_BASE}/config.json`,
|
|
38
|
+
dest: "config.json",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
url: selectOnnxModel(),
|
|
42
|
+
dest: "model.onnx",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
function isExistingAndValid(filePath) {
|
|
46
|
+
if (!existsSync(filePath))
|
|
47
|
+
return false;
|
|
48
|
+
const stat = statSync(filePath);
|
|
49
|
+
return stat.size > 0;
|
|
50
|
+
}
|
|
51
|
+
function downloadFile(url, destPath, options, fileName, redirectCount = 0) {
|
|
52
|
+
const maxRedirects = options.maxRedirects ?? 5;
|
|
53
|
+
const timeoutMs = options.timeoutMs ?? 300_000; // 5 minutes
|
|
54
|
+
if (redirectCount > maxRedirects) {
|
|
55
|
+
return Promise.reject(new Error(`Too many redirects (${maxRedirects}) for ${url}`));
|
|
56
|
+
}
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const tmpPath = destPath + ".tmp";
|
|
59
|
+
let aborted = false;
|
|
60
|
+
const cleanup = () => {
|
|
61
|
+
try {
|
|
62
|
+
if (existsSync(tmpPath))
|
|
63
|
+
unlinkSync(tmpPath);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// ignore cleanup errors
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const onSigint = () => {
|
|
70
|
+
aborted = true;
|
|
71
|
+
cleanup();
|
|
72
|
+
};
|
|
73
|
+
process.on("SIGINT", onSigint);
|
|
74
|
+
const getFunc = url.startsWith("http://") ? httpGet : httpsGet;
|
|
75
|
+
const req = getFunc(url, (response) => {
|
|
76
|
+
if (aborted)
|
|
77
|
+
return;
|
|
78
|
+
// Handle redirects (301, 302, 307, 308)
|
|
79
|
+
if (response.statusCode === 301 ||
|
|
80
|
+
response.statusCode === 302 ||
|
|
81
|
+
response.statusCode === 307 ||
|
|
82
|
+
response.statusCode === 308) {
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
process.removeListener("SIGINT", onSigint);
|
|
85
|
+
const rawLocation = response.headers.location;
|
|
86
|
+
if (!rawLocation) {
|
|
87
|
+
reject(new Error(`Redirect without location header for ${url}`));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Handle relative redirect URLs by resolving against the original URL
|
|
91
|
+
const location = rawLocation.startsWith("/") ? new URL(rawLocation, url).href : rawLocation;
|
|
92
|
+
downloadFile(location, destPath, options, fileName, redirectCount + 1)
|
|
93
|
+
.then(resolve)
|
|
94
|
+
.catch(reject);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (response.statusCode !== 200) {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
process.removeListener("SIGINT", onSigint);
|
|
100
|
+
reject(new Error(`HTTP ${response.statusCode} for ${fileName}`));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const totalSize = response.headers["content-length"]
|
|
104
|
+
? parseInt(response.headers["content-length"], 10)
|
|
105
|
+
: null;
|
|
106
|
+
let downloaded = 0;
|
|
107
|
+
response.on("data", (chunk) => {
|
|
108
|
+
downloaded += chunk.length;
|
|
109
|
+
options.onProgress?.({
|
|
110
|
+
file: fileName,
|
|
111
|
+
downloaded,
|
|
112
|
+
total: totalSize,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
const fileStream = createWriteStream(tmpPath);
|
|
116
|
+
pipeline(response, fileStream)
|
|
117
|
+
.then(() => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
process.removeListener("SIGINT", onSigint);
|
|
120
|
+
if (aborted) {
|
|
121
|
+
cleanup();
|
|
122
|
+
reject(new Error("Download aborted"));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Atomic rename
|
|
126
|
+
renameSync(tmpPath, destPath);
|
|
127
|
+
options.onFileComplete?.(fileName);
|
|
128
|
+
resolve();
|
|
129
|
+
})
|
|
130
|
+
.catch((err) => {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
process.removeListener("SIGINT", onSigint);
|
|
133
|
+
cleanup();
|
|
134
|
+
reject(err);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
aborted = true;
|
|
139
|
+
req.destroy();
|
|
140
|
+
cleanup();
|
|
141
|
+
reject(new Error(`Download timeout (${timeoutMs}ms) for ${fileName}`));
|
|
142
|
+
}, timeoutMs);
|
|
143
|
+
req.on("error", (err) => {
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
process.removeListener("SIGINT", onSigint);
|
|
146
|
+
cleanup();
|
|
147
|
+
reject(err);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export async function downloadModel(modelManager, options = {}) {
|
|
152
|
+
const modelDir = modelManager.getModelDir();
|
|
153
|
+
mkdirSync(modelDir, { recursive: true });
|
|
154
|
+
const downloaded = [];
|
|
155
|
+
const skipped = [];
|
|
156
|
+
for (const file of MODEL_FILES) {
|
|
157
|
+
const destPath = `${modelDir}/${file.dest}`;
|
|
158
|
+
if (isExistingAndValid(destPath)) {
|
|
159
|
+
skipped.push(file.dest);
|
|
160
|
+
options.onFileComplete?.(file.dest);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
await downloadFile(file.url, destPath, options, file.dest);
|
|
164
|
+
downloaded.push(file.dest);
|
|
165
|
+
}
|
|
166
|
+
return { downloaded, skipped };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=model-downloader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-downloader.js","sourceRoot":"","sources":["../../src/embedding/model-downloader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,IAAI,CAAC;AAChG,OAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,IAAI,OAAO,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAqB3C,MAAM,OAAO,GAAG,4EAA4E,CAAC;AAE7F;;;;GAIG;AACH,SAAS,eAAe;IACtB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,wBAAwB;IACnD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,GAAG,OAAO,8BAA8B,CAAC;IAClD,CAAC;IACD,iEAAiE;IACjE,OAAO,GAAG,OAAO,8BAA8B,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC;QACE,GAAG,EAAE,GAAG,OAAO,iBAAiB;QAChC,IAAI,EAAE,gBAAgB;KACvB;IACD;QACE,GAAG,EAAE,GAAG,OAAO,cAAc;QAC7B,IAAI,EAAE,aAAa;KACpB;IACD;QACE,GAAG,EAAE,eAAe,EAAE;QACtB,IAAI,EAAE,YAAY;KACnB;CACF,CAAC;AAEF,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CACnB,GAAW,EACX,QAAgB,EAChB,OAAwB,EACxB,QAAgB,EAChB,gBAAwB,CAAC;IAEzB,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,YAAY;IAE5D,IAAI,aAAa,GAAG,YAAY,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,YAAY,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC;IACtF,CAAC;IAED,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClC,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,IAAI,CAAC;gBACH,IAAI,UAAU,CAAC,OAAO,CAAC;oBAAE,UAAU,CAAC,OAAO,CAAC,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC1B,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAE/B,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAE/D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpC,IAAI,OAAO;gBAAE,OAAO;YAEpB,wCAAwC;YACxC,IACE,QAAQ,CAAC,UAAU,KAAK,GAAG;gBAC3B,QAAQ,CAAC,UAAU,KAAK,GAAG;gBAC3B,QAAQ,CAAC,UAAU,KAAK,GAAG;gBAC3B,QAAQ,CAAC,UAAU,KAAK,GAAG,EAC3B,CAAC;gBACD,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC3C,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;gBAC9C,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,wCAAwC,GAAG,EAAE,CAAC,CAAC,CAAC;oBACjE,OAAO;gBACT,CAAC;gBACD,sEAAsE;gBACtE,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;gBAC5F,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC;qBACnE,IAAI,CAAC,OAAO,CAAC;qBACb,KAAK,CAAC,MAAM,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YAED,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAChC,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC3C,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,UAAU,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC;gBACjE,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC;gBAClD,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC;gBAClD,CAAC,CAAC,IAAI,CAAC;YAET,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACpC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC;gBAC3B,OAAO,CAAC,UAAU,EAAE,CAAC;oBACnB,IAAI,EAAE,QAAQ;oBACd,UAAU;oBACV,KAAK,EAAE,SAAS;iBACjB,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAE9C,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;iBAC3B,IAAI,CAAC,GAAG,EAAE;gBACT,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC3C,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,EAAE,CAAC;oBACV,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;oBACtC,OAAO;gBACT,CAAC;gBACD,gBAAgB;gBAChB,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;gBAC9B,OAAO,CAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,CAAC;gBACnC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACb,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC3C,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,GAAG,IAAI,CAAC;YACf,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,SAAS,WAAW,QAAQ,EAAE,CAAC,CAAC,CAAC;QACzE,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3C,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,YAA0B,EAC1B,UAA2B,EAAE;IAE7B,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IAC5C,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,GAAG,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAE5C,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,OAAO,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QAED,MAAM,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -15,7 +15,7 @@ export class OnnxEmbeddingProvider {
|
|
|
15
15
|
if (this.session)
|
|
16
16
|
return this.session;
|
|
17
17
|
if (!this.modelManager.isModelAvailable(this.modelName)) {
|
|
18
|
-
throw new EmbeddingNotAvailableError(`Model "${this.modelName}" not found. Run
|
|
18
|
+
throw new EmbeddingNotAvailableError(`Model "${this.modelName}" not found. Run 'knowledgine init' to download the model automatically.`);
|
|
19
19
|
}
|
|
20
20
|
try {
|
|
21
21
|
const ort = await import("onnxruntime-node");
|
|
@@ -30,7 +30,7 @@ export class OnnxEmbeddingProvider {
|
|
|
30
30
|
if (this.tokenizer)
|
|
31
31
|
return this.tokenizer;
|
|
32
32
|
if (!this.modelManager.isModelAvailable(this.modelName)) {
|
|
33
|
-
throw new EmbeddingNotAvailableError(`Model "${this.modelName}" not found. Run
|
|
33
|
+
throw new EmbeddingNotAvailableError(`Model "${this.modelName}" not found. Run 'knowledgine init' to download the model automatically.`);
|
|
34
34
|
}
|
|
35
35
|
this.tokenizer = new WordPieceTokenizer(this.modelManager.getTokenizerPath(this.modelName));
|
|
36
36
|
return this.tokenizer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onnx-embedding-provider.js","sourceRoot":"","sources":["../../src/embedding/onnx-embedding-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE1E,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,MAAM,OAAO,qBAAqB;IACxB,OAAO,GAA4B,IAAI,CAAC;IACxC,SAAS,GAA8B,IAAI,CAAC;IAC5C,SAAS,CAAS;IAClB,YAAY,CAAe;IAEnC,YAAY,YAAoB,kBAAkB,EAAE,YAA2B;QAC7E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,YAAY,IAAI,IAAI,YAAY,EAAE,CAAC;IACzD,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC;QAEtC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,0BAA0B,CAClC,UAAU,IAAI,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"onnx-embedding-provider.js","sourceRoot":"","sources":["../../src/embedding/onnx-embedding-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FeedbackRepository } from "./feedback-repository.js";
|
|
2
|
+
export interface TypeOverride {
|
|
3
|
+
name: string;
|
|
4
|
+
fromType: string;
|
|
5
|
+
toType: string;
|
|
6
|
+
}
|
|
7
|
+
export interface WhitelistEntry {
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ExtractionRules {
|
|
12
|
+
version: number;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
stopWords: {
|
|
15
|
+
added: string[];
|
|
16
|
+
};
|
|
17
|
+
typeOverrides: TypeOverride[];
|
|
18
|
+
entityBlacklist: string[];
|
|
19
|
+
entityWhitelist: WhitelistEntry[];
|
|
20
|
+
}
|
|
21
|
+
export declare class FeedbackLearner {
|
|
22
|
+
private feedbackRepository;
|
|
23
|
+
private rulesPath;
|
|
24
|
+
constructor(feedbackRepository: FeedbackRepository, rulesPath: string);
|
|
25
|
+
applyFeedback(feedbackId: number): void;
|
|
26
|
+
loadRules(): ExtractionRules;
|
|
27
|
+
private saveRules;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=feedback-learner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-learner.d.ts","sourceRoot":"","sources":["../../src/feedback/feedback-learner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,EAAE,cAAc,EAAE,CAAC;CACnC;AAaD,qBAAa,eAAe;IAExB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,SAAS;gBADT,kBAAkB,EAAE,kBAAkB,EACtC,SAAS,EAAE,MAAM;IAG3B,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAyDvC,SAAS,IAAI,eAAe;IAS5B,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, renameSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { FeedbackRepository } from "./feedback-repository.js";
|
|
4
|
+
function createEmptyRules() {
|
|
5
|
+
return {
|
|
6
|
+
version: 1,
|
|
7
|
+
updatedAt: new Date().toISOString(),
|
|
8
|
+
stopWords: { added: [] },
|
|
9
|
+
typeOverrides: [],
|
|
10
|
+
entityBlacklist: [],
|
|
11
|
+
entityWhitelist: [],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export class FeedbackLearner {
|
|
15
|
+
feedbackRepository;
|
|
16
|
+
rulesPath;
|
|
17
|
+
constructor(feedbackRepository, rulesPath) {
|
|
18
|
+
this.feedbackRepository = feedbackRepository;
|
|
19
|
+
this.rulesPath = rulesPath;
|
|
20
|
+
}
|
|
21
|
+
applyFeedback(feedbackId) {
|
|
22
|
+
const feedback = this.feedbackRepository.getById(feedbackId);
|
|
23
|
+
if (!feedback) {
|
|
24
|
+
throw new Error(`Feedback record not found: id=${feedbackId}`);
|
|
25
|
+
}
|
|
26
|
+
const rules = this.loadRules();
|
|
27
|
+
switch (feedback.errorType) {
|
|
28
|
+
case "false_positive":
|
|
29
|
+
if (!rules.entityBlacklist.includes(feedback.entityName)) {
|
|
30
|
+
rules.entityBlacklist.push(feedback.entityName);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
case "wrong_type":
|
|
34
|
+
if (!feedback.entityType || !feedback.correctType) {
|
|
35
|
+
throw new Error("wrong_type feedback requires both entityType and correctType");
|
|
36
|
+
}
|
|
37
|
+
// Check for existing override for same entity name
|
|
38
|
+
const existingIdx = rules.typeOverrides.findIndex((o) => o.name === feedback.entityName);
|
|
39
|
+
const override = {
|
|
40
|
+
name: feedback.entityName,
|
|
41
|
+
fromType: feedback.entityType,
|
|
42
|
+
toType: feedback.correctType,
|
|
43
|
+
};
|
|
44
|
+
if (existingIdx >= 0) {
|
|
45
|
+
rules.typeOverrides[existingIdx] = override;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
rules.typeOverrides.push(override);
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
51
|
+
case "missed_entity": {
|
|
52
|
+
const entityType = feedback.correctType ?? feedback.entityType ?? "technology";
|
|
53
|
+
// Check for existing whitelist entry for same entity name
|
|
54
|
+
const existingWlIdx = rules.entityWhitelist.findIndex((w) => w.name === feedback.entityName);
|
|
55
|
+
const entry = {
|
|
56
|
+
name: feedback.entityName,
|
|
57
|
+
type: entityType,
|
|
58
|
+
};
|
|
59
|
+
if (existingWlIdx >= 0) {
|
|
60
|
+
rules.entityWhitelist[existingWlIdx] = entry;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
rules.entityWhitelist.push(entry);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
rules.updatedAt = new Date().toISOString();
|
|
69
|
+
this.saveRules(rules);
|
|
70
|
+
this.feedbackRepository.updateStatus(feedbackId, "applied");
|
|
71
|
+
}
|
|
72
|
+
loadRules() {
|
|
73
|
+
try {
|
|
74
|
+
const content = readFileSync(this.rulesPath, "utf-8");
|
|
75
|
+
return JSON.parse(content);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return createEmptyRules();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
saveRules(rules) {
|
|
82
|
+
const dir = dirname(this.rulesPath);
|
|
83
|
+
const tmpPath = join(dir, `.extraction-rules.tmp.${process.pid}.json`);
|
|
84
|
+
writeFileSync(tmpPath, JSON.stringify(rules, null, 2), "utf-8");
|
|
85
|
+
renameSync(tmpPath, this.rulesPath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=feedback-learner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-learner.js","sourceRoot":"","sources":["../../src/feedback/feedback-learner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { FeedbackErrorType, FeedbackStatus } from "../types.js";
|
|
3
|
+
export interface CreateFeedbackInput {
|
|
4
|
+
entityName: string;
|
|
5
|
+
errorType: FeedbackErrorType;
|
|
6
|
+
entityType?: string;
|
|
7
|
+
correctType?: string;
|
|
8
|
+
noteId?: number;
|
|
9
|
+
details?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FeedbackRecord {
|
|
12
|
+
id: number;
|
|
13
|
+
entityName: string;
|
|
14
|
+
entityType: string | null;
|
|
15
|
+
errorType: FeedbackErrorType;
|
|
16
|
+
correctType: string | null;
|
|
17
|
+
noteId: number | null;
|
|
18
|
+
details: string | null;
|
|
19
|
+
status: FeedbackStatus;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
appliedAt: string | null;
|
|
22
|
+
}
|
|
23
|
+
export declare class FeedbackRepository {
|
|
24
|
+
private db;
|
|
25
|
+
constructor(db: Database.Database);
|
|
26
|
+
create(input: CreateFeedbackInput): FeedbackRecord;
|
|
27
|
+
getById(id: number): FeedbackRecord | undefined;
|
|
28
|
+
list(options?: {
|
|
29
|
+
status?: string;
|
|
30
|
+
limit?: number;
|
|
31
|
+
}): FeedbackRecord[];
|
|
32
|
+
updateStatus(id: number, status: string): void;
|
|
33
|
+
delete(id: number): void;
|
|
34
|
+
getStats(): {
|
|
35
|
+
total: number;
|
|
36
|
+
pending: number;
|
|
37
|
+
applied: number;
|
|
38
|
+
dismissed: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=feedback-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-repository.d.ts","sourceRoot":"","sources":["../../src/feedback/feedback-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKrE,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA8BD,qBAAa,kBAAkB;IACjB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAEzC,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc;IAsBlD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAO/C,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,cAAc,EAAE;IAyBrE,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAe9C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQxB,QAAQ,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;CAcnF"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const VALID_ERROR_TYPES = ["false_positive", "wrong_type", "missed_entity"];
|
|
2
|
+
const VALID_STATUSES = ["pending", "applied", "dismissed"];
|
|
3
|
+
function rowToRecord(row) {
|
|
4
|
+
return {
|
|
5
|
+
id: row.id,
|
|
6
|
+
entityName: row.entity_name,
|
|
7
|
+
entityType: row.entity_type,
|
|
8
|
+
errorType: row.error_type,
|
|
9
|
+
correctType: row.correct_type,
|
|
10
|
+
noteId: row.note_id,
|
|
11
|
+
details: row.details,
|
|
12
|
+
status: row.status,
|
|
13
|
+
createdAt: row.created_at,
|
|
14
|
+
appliedAt: row.applied_at,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export class FeedbackRepository {
|
|
18
|
+
db;
|
|
19
|
+
constructor(db) {
|
|
20
|
+
this.db = db;
|
|
21
|
+
}
|
|
22
|
+
create(input) {
|
|
23
|
+
if (!VALID_ERROR_TYPES.includes(input.errorType)) {
|
|
24
|
+
throw new Error(`Invalid error_type: "${input.errorType}". Must be one of: ${VALID_ERROR_TYPES.join(", ")}`);
|
|
25
|
+
}
|
|
26
|
+
const stmt = this.db.prepare(`
|
|
27
|
+
INSERT INTO extraction_feedback (entity_name, entity_type, error_type, correct_type, note_id, details)
|
|
28
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
29
|
+
`);
|
|
30
|
+
const info = stmt.run(input.entityName, input.entityType ?? null, input.errorType, input.correctType ?? null, input.noteId ?? null, input.details ?? null);
|
|
31
|
+
return this.getById(Number(info.lastInsertRowid));
|
|
32
|
+
}
|
|
33
|
+
getById(id) {
|
|
34
|
+
const row = this.db.prepare("SELECT * FROM extraction_feedback WHERE id = ?").get(id);
|
|
35
|
+
return row ? rowToRecord(row) : undefined;
|
|
36
|
+
}
|
|
37
|
+
list(options) {
|
|
38
|
+
const conditions = [];
|
|
39
|
+
const params = [];
|
|
40
|
+
if (options?.status) {
|
|
41
|
+
if (!VALID_STATUSES.includes(options.status)) {
|
|
42
|
+
throw new Error(`Invalid status: "${options.status}". Must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
43
|
+
}
|
|
44
|
+
conditions.push("status = ?");
|
|
45
|
+
params.push(options.status);
|
|
46
|
+
}
|
|
47
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
48
|
+
const limit = options?.limit ?? 100;
|
|
49
|
+
params.push(limit);
|
|
50
|
+
const rows = this.db
|
|
51
|
+
.prepare(`SELECT * FROM extraction_feedback ${where} ORDER BY created_at DESC LIMIT ?`)
|
|
52
|
+
.all(...params);
|
|
53
|
+
return rows.map(rowToRecord);
|
|
54
|
+
}
|
|
55
|
+
updateStatus(id, status) {
|
|
56
|
+
if (!VALID_STATUSES.includes(status)) {
|
|
57
|
+
throw new Error(`Invalid status: "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
58
|
+
}
|
|
59
|
+
const appliedAt = status === "applied" ? new Date().toISOString() : null;
|
|
60
|
+
const info = this.db
|
|
61
|
+
.prepare("UPDATE extraction_feedback SET status = ?, applied_at = ? WHERE id = ?")
|
|
62
|
+
.run(status, appliedAt, id);
|
|
63
|
+
if (info.changes === 0) {
|
|
64
|
+
throw new Error(`Feedback record not found: id=${id}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
delete(id) {
|
|
68
|
+
const info = this.db.prepare("DELETE FROM extraction_feedback WHERE id = ?").run(id);
|
|
69
|
+
if (info.changes === 0) {
|
|
70
|
+
throw new Error(`Feedback record not found: id=${id}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
getStats() {
|
|
74
|
+
const rows = this.db
|
|
75
|
+
.prepare(`SELECT status, COUNT(*) as count FROM extraction_feedback GROUP BY status`)
|
|
76
|
+
.all();
|
|
77
|
+
const stats = { total: 0, pending: 0, applied: 0, dismissed: 0 };
|
|
78
|
+
for (const row of rows) {
|
|
79
|
+
stats.total += row.count;
|
|
80
|
+
if (row.status === "pending")
|
|
81
|
+
stats.pending = row.count;
|
|
82
|
+
else if (row.status === "applied")
|
|
83
|
+
stats.applied = row.count;
|
|
84
|
+
else if (row.status === "dismissed")
|
|
85
|
+
stats.dismissed = row.count;
|
|
86
|
+
}
|
|
87
|
+
return stats;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=feedback-repository.js.map
|