n2-qln 3.4.2 → 4.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/README.ko.md +459 -470
- package/README.md +459 -490
- package/dist/index.d.ts +3 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/{lib → dist/lib}/config.js +23 -27
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/embedding.d.ts +27 -0
- package/{lib → dist/lib}/embedding.js +39 -47
- package/dist/lib/embedding.js.map +1 -0
- package/dist/lib/executor.d.ts +57 -0
- package/dist/lib/executor.js +175 -0
- package/dist/lib/executor.js.map +1 -0
- package/dist/lib/mcp-discovery.d.ts +83 -0
- package/dist/lib/mcp-discovery.js +203 -0
- package/dist/lib/mcp-discovery.js.map +1 -0
- package/dist/lib/provider-loader.d.ts +13 -0
- package/dist/lib/provider-loader.js +146 -0
- package/dist/lib/provider-loader.js.map +1 -0
- package/dist/lib/registry.d.ts +38 -0
- package/{lib → dist/lib}/registry.js +82 -92
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/router.d.ts +63 -0
- package/{lib → dist/lib}/router.js +75 -117
- package/dist/lib/router.js.map +1 -0
- package/dist/lib/schema.d.ts +20 -0
- package/{lib → dist/lib}/schema.js +38 -30
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/store.d.ts +37 -0
- package/dist/lib/store.js +207 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/validator.d.ts +37 -0
- package/dist/lib/validator.js +114 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/lib/vector-index.d.ts +37 -0
- package/{lib → dist/lib}/vector-index.js +19 -36
- package/dist/lib/vector-index.js.map +1 -0
- package/dist/tools/qln-call.d.ts +41 -0
- package/dist/tools/qln-call.js +353 -0
- package/dist/tools/qln-call.js.map +1 -0
- package/dist/tools/qln-helpers.d.ts +55 -0
- package/dist/tools/qln-helpers.js +88 -0
- package/dist/tools/qln-helpers.js.map +1 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/index.js +3 -79
- package/package.json +11 -4
- package/.github/FUNDING.yml +0 -3
- package/docs/README.md +0 -2
- package/docs/architecture.png +0 -0
- package/lib/executor.js +0 -104
- package/lib/provider-loader.js +0 -126
- package/lib/store.js +0 -217
- package/lib/validator.js +0 -171
- package/tools/qln-call.js +0 -257
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
// QLN — Query Layer Network MCP server entry point
|
|
8
|
+
// Semantic tool dispatcher: route 1000 tools through 1 router
|
|
9
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
10
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11
|
+
const zod_1 = require("zod");
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
// Core modules
|
|
14
|
+
const config_1 = require("./lib/config");
|
|
15
|
+
const store_1 = require("./lib/store");
|
|
16
|
+
const embedding_1 = require("./lib/embedding");
|
|
17
|
+
const registry_1 = require("./lib/registry");
|
|
18
|
+
const vector_index_1 = require("./lib/vector-index");
|
|
19
|
+
const router_1 = require("./lib/router");
|
|
20
|
+
const executor_1 = require("./lib/executor");
|
|
21
|
+
const provider_loader_1 = require("./lib/provider-loader");
|
|
22
|
+
const mcp_discovery_1 = require("./lib/mcp-discovery");
|
|
23
|
+
// MCP Tool (unified)
|
|
24
|
+
const qln_call_1 = require("./tools/qln-call");
|
|
25
|
+
/** Load provider manifests into registry */
|
|
26
|
+
function initProviders(config, registry) {
|
|
27
|
+
if (config.providers?.enabled === false)
|
|
28
|
+
return;
|
|
29
|
+
const provDir = config.providers?.dir || path_1.default.join(__dirname, '..', 'providers');
|
|
30
|
+
const provResult = (0, provider_loader_1.loadProviders)(provDir, registry);
|
|
31
|
+
if (provResult.loaded > 0) {
|
|
32
|
+
console.error(`[QLN] Providers: ${provResult.loaded} tools from ${provResult.details.filter(d => d.status === 'loaded').length} files`);
|
|
33
|
+
}
|
|
34
|
+
if (provResult.failed > 0) {
|
|
35
|
+
console.error(`[QLN] Provider warnings: ${provResult.failed} files failed to load`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Schedule async embedding precompute (non-blocking) */
|
|
39
|
+
function scheduleEmbeddings(embedding, registry, router) {
|
|
40
|
+
if (!embedding)
|
|
41
|
+
return;
|
|
42
|
+
setImmediate(async () => {
|
|
43
|
+
try {
|
|
44
|
+
await registry.precomputeEmbeddings();
|
|
45
|
+
router.buildIndex();
|
|
46
|
+
}
|
|
47
|
+
catch { /* Ollama not available — Stage 1+2 still work */ }
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function main() {
|
|
51
|
+
const config = (0, config_1.loadConfig)();
|
|
52
|
+
// 1. Core engine initialization
|
|
53
|
+
const store = new store_1.Store(config.dataDir);
|
|
54
|
+
await store.init();
|
|
55
|
+
const embedding = config.embedding?.enabled
|
|
56
|
+
? new embedding_1.Embedding(config.embedding)
|
|
57
|
+
: null;
|
|
58
|
+
const registry = new registry_1.Registry(store, embedding);
|
|
59
|
+
registry.load();
|
|
60
|
+
// 1.5. Provider auto-indexing
|
|
61
|
+
initProviders(config, registry);
|
|
62
|
+
const vectorIndex = new vector_index_1.VectorIndex();
|
|
63
|
+
const router = new router_1.Router(registry, vectorIndex, embedding, config.search?.sourceWeights || {});
|
|
64
|
+
const executor = new executor_1.Executor(config.executor || {});
|
|
65
|
+
const discovery = new mcp_discovery_1.McpDiscovery(registry, router);
|
|
66
|
+
// 2. Precompute embeddings (async, non-blocking)
|
|
67
|
+
scheduleEmbeddings(embedding, registry, router);
|
|
68
|
+
// 3. Create MCP server + register tool
|
|
69
|
+
const pkg = require('../package.json');
|
|
70
|
+
const server = new mcp_js_1.McpServer({ name: 'n2-qln', version: pkg.version });
|
|
71
|
+
(0, qln_call_1.registerQlnCall)(server, zod_1.z, router, executor, registry, discovery);
|
|
72
|
+
// 4. Connect stdio transport
|
|
73
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
74
|
+
await server.connect(transport);
|
|
75
|
+
// 5. Hot Reload: watch providers dir for changes
|
|
76
|
+
if (config.providers?.enabled !== false) {
|
|
77
|
+
const provDir = config.providers?.dir || path_1.default.join(__dirname, '..', 'providers');
|
|
78
|
+
(0, provider_loader_1.watchProviders)(provDir, registry, () => {
|
|
79
|
+
router.buildIndex();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
main().catch((err) => {
|
|
84
|
+
console.error(`[QLN] Fatal: ${err.message}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { QLNConfig } from '../types';
|
|
2
|
+
/** Default configuration */
|
|
3
|
+
declare const defaults: QLNConfig;
|
|
4
|
+
/**
|
|
5
|
+
* Load config — apply config.local.js overrides on top of defaults.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadConfig(): QLNConfig;
|
|
8
|
+
export { defaults };
|
|
9
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1,71 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.defaults = void 0;
|
|
7
|
+
exports.loadConfig = loadConfig;
|
|
1
8
|
// QLN — Config loader (default + local deep merge)
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
5
11
|
/** Default configuration */
|
|
6
12
|
const defaults = {
|
|
7
|
-
|
|
8
|
-
dataDir: path.join(__dirname, '..', 'data'),
|
|
9
|
-
|
|
10
|
-
/** Embedding configuration */
|
|
13
|
+
dataDir: path_1.default.join(__dirname, '..', '..', 'data'),
|
|
11
14
|
embedding: {
|
|
12
15
|
enabled: true,
|
|
13
16
|
model: 'nomic-embed-text',
|
|
14
17
|
endpoint: 'http://127.0.0.1:11434',
|
|
15
18
|
},
|
|
16
|
-
|
|
17
|
-
/** Tool execution configuration */
|
|
18
19
|
executor: {
|
|
19
20
|
httpEndpoint: null,
|
|
20
21
|
timeout: 20000,
|
|
21
22
|
},
|
|
22
|
-
|
|
23
|
-
/** Provider manifest auto-indexing */
|
|
24
23
|
providers: {
|
|
25
24
|
enabled: true,
|
|
26
|
-
dir:
|
|
25
|
+
dir: path_1.default.join(__dirname, '..', '..', 'providers'),
|
|
27
26
|
},
|
|
28
|
-
|
|
29
|
-
/** Search configuration */
|
|
30
27
|
search: {
|
|
31
28
|
defaultTopK: 5,
|
|
32
29
|
threshold: 0.1,
|
|
30
|
+
sourceWeights: { mcp: 1.2, plugin: 1.0, local: 0.8 },
|
|
33
31
|
},
|
|
34
32
|
};
|
|
35
|
-
|
|
33
|
+
exports.defaults = defaults;
|
|
36
34
|
/**
|
|
37
35
|
* Load config — apply config.local.js overrides on top of defaults.
|
|
38
|
-
* @returns {object} Merged config
|
|
39
36
|
*/
|
|
40
37
|
function loadConfig() {
|
|
41
38
|
const config = JSON.parse(JSON.stringify(defaults));
|
|
42
|
-
const localPath =
|
|
43
|
-
|
|
44
|
-
if (fs.existsSync(localPath)) {
|
|
39
|
+
const localPath = path_1.default.join(__dirname, '..', '..', 'config.local.js');
|
|
40
|
+
if (fs_1.default.existsSync(localPath)) {
|
|
45
41
|
try {
|
|
42
|
+
// config.local.js is a CJS module — require is intentional
|
|
46
43
|
const local = require(localPath);
|
|
47
44
|
deepMerge(config, local);
|
|
48
|
-
}
|
|
49
|
-
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
48
|
+
console.warn(`[QLN] config.local.js load failed: ${msg}`);
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
return config;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
53
|
/**
|
|
56
54
|
* Deep merge (merge source into target).
|
|
57
|
-
* @param {object} target
|
|
58
|
-
* @param {object} source
|
|
59
55
|
*/
|
|
60
56
|
function deepMerge(target, source) {
|
|
61
57
|
for (const key of Object.keys(source)) {
|
|
62
58
|
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])
|
|
63
59
|
&& target[key] && typeof target[key] === 'object') {
|
|
64
60
|
deepMerge(target[key], source[key]);
|
|
65
|
-
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
66
63
|
target[key] = source[key];
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
}
|
|
70
|
-
|
|
71
|
-
module.exports = { loadConfig, defaults };
|
|
67
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":";;;;;;AA+BA,gCAeC;AA9CD,mDAAmD;AACnD,gDAAwB;AACxB,4CAAoB;AAGpB,4BAA4B;AAC5B,MAAM,QAAQ,GAAc;IAC1B,OAAO,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;IACjD,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,kBAAkB;QACzB,QAAQ,EAAE,wBAAwB;KACnC;IACD,QAAQ,EAAE;QACR,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,KAAK;KACf;IACD,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC;KACnD;IACD,MAAM,EAAE;QACN,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,GAAG;QACd,aAAa,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;KACrD;CACF,CAAC;AAoCO,4BAAQ;AAlCjB;;GAEG;AACH,SAAgB,UAAU;IACxB,MAAM,MAAM,GAAc,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC;IAEtE,IAAI,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,2DAA2D;YAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAuB,CAAC;YACvD,SAAS,CAAC,MAA4C,EAAE,KAA2C,CAAC,CAAC;QACvG,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,sCAAsC,GAAG,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,MAA+B,EAAE,MAA+B;IACjF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;eAC1E,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC;YACtD,SAAS,CAAC,MAAM,CAAC,GAAG,CAA4B,EAAE,MAAM,CAAC,GAAG,CAA4B,CAAC,CAAC;QAC5F,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface EmbeddingConfig {
|
|
2
|
+
model?: string;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Ollama-based local embedding engine.
|
|
7
|
+
* Graceful degradation when unavailable — Stage 1+2 still work.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Embedding {
|
|
10
|
+
private model;
|
|
11
|
+
private endpoint;
|
|
12
|
+
dimensions: number | null;
|
|
13
|
+
private _available;
|
|
14
|
+
constructor(config?: EmbeddingConfig);
|
|
15
|
+
/** Check Ollama availability (cached). */
|
|
16
|
+
isAvailable(): Promise<boolean>;
|
|
17
|
+
/** Generate vector embedding from text. */
|
|
18
|
+
embed(text: string): Promise<number[]>;
|
|
19
|
+
/** Batch embedding generation. */
|
|
20
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
21
|
+
/** Cosine similarity between two vectors. */
|
|
22
|
+
cosineSimilarity(a: number[], b: number[]): number;
|
|
23
|
+
/** @internal HTTP POST to Ollama API */
|
|
24
|
+
private _post;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=embedding.d.ts.map
|
|
@@ -1,57 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Embedding = void 0;
|
|
1
7
|
// QLN — Embedding engine (Ollama nomic-embed-text)
|
|
2
8
|
// Vector embedding generation for semantic search (Stage 3)
|
|
3
|
-
const
|
|
4
|
-
|
|
9
|
+
const http_1 = __importDefault(require("http"));
|
|
5
10
|
/**
|
|
6
11
|
* Ollama-based local embedding engine.
|
|
7
12
|
* Graceful degradation when unavailable — Stage 1+2 still work.
|
|
8
13
|
*/
|
|
9
14
|
class Embedding {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*/
|
|
15
|
+
model;
|
|
16
|
+
endpoint;
|
|
17
|
+
dimensions;
|
|
18
|
+
_available;
|
|
15
19
|
constructor(config = {}) {
|
|
16
20
|
this.model = config.model || 'nomic-embed-text';
|
|
17
21
|
this.endpoint = config.endpoint || 'http://127.0.0.1:11434';
|
|
18
22
|
this.dimensions = null;
|
|
19
23
|
this._available = null;
|
|
20
24
|
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Check Ollama availability (cached).
|
|
24
|
-
* @returns {Promise<boolean>}
|
|
25
|
-
*/
|
|
25
|
+
/** Check Ollama availability (cached). */
|
|
26
26
|
async isAvailable() {
|
|
27
|
-
if (this._available !== null)
|
|
27
|
+
if (this._available !== null)
|
|
28
|
+
return this._available;
|
|
28
29
|
try {
|
|
29
30
|
const vec = await this.embed('test');
|
|
30
31
|
this._available = vec.length > 0;
|
|
31
32
|
this.dimensions = vec.length;
|
|
32
33
|
return this._available;
|
|
33
|
-
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
34
36
|
this._available = false;
|
|
35
37
|
return false;
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Generate vector embedding from text.
|
|
41
|
-
* @param {string} text
|
|
42
|
-
* @returns {Promise<number[]>}
|
|
43
|
-
*/
|
|
40
|
+
/** Generate vector embedding from text. */
|
|
44
41
|
async embed(text) {
|
|
45
|
-
if (!text || text.trim().length === 0)
|
|
42
|
+
if (!text || text.trim().length === 0)
|
|
43
|
+
return [];
|
|
46
44
|
const input = text.length > 2000 ? text.slice(0, 2000) : text;
|
|
47
|
-
|
|
48
45
|
for (const apiPath of ['/api/embeddings', '/api/embed']) {
|
|
49
46
|
try {
|
|
50
47
|
const body = apiPath === '/api/embeddings'
|
|
51
48
|
? { model: this.model, prompt: input }
|
|
52
49
|
: { model: this.model, input: input };
|
|
53
50
|
const result = await this._post(apiPath, body);
|
|
54
|
-
|
|
55
51
|
if (result.embedding && Array.isArray(result.embedding)) {
|
|
56
52
|
this.dimensions = result.embedding.length;
|
|
57
53
|
return result.embedding;
|
|
@@ -60,16 +56,14 @@ class Embedding {
|
|
|
60
56
|
this.dimensions = result.embeddings[0].length;
|
|
61
57
|
return result.embeddings[0];
|
|
62
58
|
}
|
|
63
|
-
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
64
63
|
}
|
|
65
64
|
return [];
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Batch embedding generation.
|
|
70
|
-
* @param {string[]} texts
|
|
71
|
-
* @returns {Promise<number[][]>}
|
|
72
|
-
*/
|
|
66
|
+
/** Batch embedding generation. */
|
|
73
67
|
async embedBatch(texts) {
|
|
74
68
|
const results = [];
|
|
75
69
|
for (const text of texts) {
|
|
@@ -77,15 +71,10 @@ class Embedding {
|
|
|
77
71
|
}
|
|
78
72
|
return results;
|
|
79
73
|
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Cosine similarity between two vectors.
|
|
83
|
-
* @param {number[]} a
|
|
84
|
-
* @param {number[]} b
|
|
85
|
-
* @returns {number} 0~1
|
|
86
|
-
*/
|
|
74
|
+
/** Cosine similarity between two vectors. */
|
|
87
75
|
cosineSimilarity(a, b) {
|
|
88
|
-
if (!a || !b || a.length === 0 || a.length !== b.length)
|
|
76
|
+
if (!a || !b || a.length === 0 || a.length !== b.length)
|
|
77
|
+
return 0;
|
|
89
78
|
let dot = 0, normA = 0, normB = 0;
|
|
90
79
|
for (let i = 0; i < a.length; i++) {
|
|
91
80
|
dot += a[i] * b[i];
|
|
@@ -95,8 +84,7 @@ class Embedding {
|
|
|
95
84
|
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
96
85
|
return denom === 0 ? 0 : dot / denom;
|
|
97
86
|
}
|
|
98
|
-
|
|
99
|
-
/** @private HTTP POST to Ollama API */
|
|
87
|
+
/** @internal HTTP POST to Ollama API */
|
|
100
88
|
_post(apiPath, body) {
|
|
101
89
|
return new Promise((resolve, reject) => {
|
|
102
90
|
const url = new URL(this.endpoint);
|
|
@@ -108,16 +96,20 @@ class Embedding {
|
|
|
108
96
|
headers: { 'Content-Type': 'application/json' },
|
|
109
97
|
timeout: 30000,
|
|
110
98
|
};
|
|
111
|
-
const req =
|
|
99
|
+
const req = http_1.default.request(options, (res) => {
|
|
112
100
|
let data = '';
|
|
113
|
-
res.on('data', chunk => { data += chunk; });
|
|
101
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
114
102
|
res.on('end', () => {
|
|
115
|
-
if (res.statusCode >= 400) {
|
|
103
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
116
104
|
reject(new Error(`Ollama ${res.statusCode}: ${data.slice(0, 200)}`));
|
|
117
105
|
return;
|
|
118
106
|
}
|
|
119
|
-
try {
|
|
120
|
-
|
|
107
|
+
try {
|
|
108
|
+
resolve(JSON.parse(data));
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
reject(new Error(`Invalid JSON: ${data.slice(0, 100)}`));
|
|
112
|
+
}
|
|
121
113
|
});
|
|
122
114
|
});
|
|
123
115
|
req.on('error', reject);
|
|
@@ -127,5 +119,5 @@ class Embedding {
|
|
|
127
119
|
});
|
|
128
120
|
}
|
|
129
121
|
}
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
exports.Embedding = Embedding;
|
|
123
|
+
//# sourceMappingURL=embedding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.js","sourceRoot":"","sources":["../../src/lib/embedding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ExecResult, ToolHandler, CircuitBreakerConfig, CircuitState } from '../types';
|
|
2
|
+
interface ExecutorConfig {
|
|
3
|
+
httpEndpoint?: string | null;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
circuitBreaker?: Partial<CircuitBreakerConfig>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Tool executor — HTTP proxy + local function calls + Circuit Breaker.
|
|
9
|
+
*
|
|
10
|
+
* Execution priority:
|
|
11
|
+
* 1. Circuit breaker check (reject if open)
|
|
12
|
+
* 2. Local registered handler (via addHandler)
|
|
13
|
+
* 3. HTTP proxy (when endpoint configured)
|
|
14
|
+
*
|
|
15
|
+
* Circuit Breaker states:
|
|
16
|
+
* closed → normal, requests pass through
|
|
17
|
+
* open → tool disabled, fast-fail (after N consecutive failures)
|
|
18
|
+
* half-open → recovery attempt (after recovery timeout)
|
|
19
|
+
*/
|
|
20
|
+
export declare class Executor {
|
|
21
|
+
private _httpEndpoint;
|
|
22
|
+
private _timeout;
|
|
23
|
+
private _handlers;
|
|
24
|
+
private _circuits;
|
|
25
|
+
private _cbConfig;
|
|
26
|
+
constructor(config?: ExecutorConfig);
|
|
27
|
+
/** Register a local tool handler. */
|
|
28
|
+
addHandler(name: string, handler: ToolHandler): void;
|
|
29
|
+
/** Execute a tool (with circuit breaker protection). */
|
|
30
|
+
exec(name: string, args?: Record<string, unknown>): Promise<ExecResult>;
|
|
31
|
+
/** @internal Route to handler or HTTP proxy. */
|
|
32
|
+
private _dispatch;
|
|
33
|
+
/** @internal Circuit breaker gate — throws if tool is disabled. */
|
|
34
|
+
private _checkCircuit;
|
|
35
|
+
/** Get circuit breaker state for a tool. */
|
|
36
|
+
getCircuitState(name: string): {
|
|
37
|
+
state: CircuitState;
|
|
38
|
+
failures: number;
|
|
39
|
+
};
|
|
40
|
+
/** Manually reset circuit breaker for a tool. */
|
|
41
|
+
resetCircuit(name: string): void;
|
|
42
|
+
/** Get all tripped circuits (for stats). */
|
|
43
|
+
getTrippedCircuits(): Array<{
|
|
44
|
+
name: string;
|
|
45
|
+
failures: number;
|
|
46
|
+
state: CircuitState;
|
|
47
|
+
}>;
|
|
48
|
+
/** Dynamically set HTTP endpoint. */
|
|
49
|
+
setHttpEndpoint(endpoint: string): void;
|
|
50
|
+
private _getCircuit;
|
|
51
|
+
private _recordSuccess;
|
|
52
|
+
private _recordFailure;
|
|
53
|
+
/** @internal HTTP POST /call → tool execution */
|
|
54
|
+
private _execHttp;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Executor = void 0;
|
|
7
|
+
// QLN — L3 tool executor with Circuit Breaker
|
|
8
|
+
// Execute tools via HTTP (localhost) or registered local handlers
|
|
9
|
+
// Circuit Breaker: auto-disable tools after consecutive failures
|
|
10
|
+
const http_1 = __importDefault(require("http"));
|
|
11
|
+
/**
|
|
12
|
+
* Tool executor — HTTP proxy + local function calls + Circuit Breaker.
|
|
13
|
+
*
|
|
14
|
+
* Execution priority:
|
|
15
|
+
* 1. Circuit breaker check (reject if open)
|
|
16
|
+
* 2. Local registered handler (via addHandler)
|
|
17
|
+
* 3. HTTP proxy (when endpoint configured)
|
|
18
|
+
*
|
|
19
|
+
* Circuit Breaker states:
|
|
20
|
+
* closed → normal, requests pass through
|
|
21
|
+
* open → tool disabled, fast-fail (after N consecutive failures)
|
|
22
|
+
* half-open → recovery attempt (after recovery timeout)
|
|
23
|
+
*/
|
|
24
|
+
class Executor {
|
|
25
|
+
_httpEndpoint;
|
|
26
|
+
_timeout;
|
|
27
|
+
_handlers;
|
|
28
|
+
_circuits;
|
|
29
|
+
_cbConfig;
|
|
30
|
+
constructor(config = {}) {
|
|
31
|
+
this._httpEndpoint = config.httpEndpoint || null;
|
|
32
|
+
this._timeout = config.timeout || 20000;
|
|
33
|
+
this._handlers = new Map();
|
|
34
|
+
this._circuits = new Map();
|
|
35
|
+
this._cbConfig = {
|
|
36
|
+
failureThreshold: config.circuitBreaker?.failureThreshold ?? 3,
|
|
37
|
+
recoveryTimeout: config.circuitBreaker?.recoveryTimeout ?? 60000,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Register a local tool handler. */
|
|
41
|
+
addHandler(name, handler) {
|
|
42
|
+
this._handlers.set(name, handler);
|
|
43
|
+
}
|
|
44
|
+
/** Execute a tool (with circuit breaker protection). */
|
|
45
|
+
async exec(name, args = {}) {
|
|
46
|
+
const circuit = this._checkCircuit(name);
|
|
47
|
+
const t0 = Date.now();
|
|
48
|
+
circuit.lastAttempt = t0;
|
|
49
|
+
try {
|
|
50
|
+
const result = await this._dispatch(name, args, t0);
|
|
51
|
+
this._recordSuccess(name);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (!err.__qlnConfigError) {
|
|
56
|
+
this._recordFailure(name);
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** @internal Route to handler or HTTP proxy. */
|
|
62
|
+
async _dispatch(name, args, t0) {
|
|
63
|
+
if (this._handlers.has(name)) {
|
|
64
|
+
const output = await this._handlers.get(name)(args);
|
|
65
|
+
return { result: output, source: 'local', elapsed: Date.now() - t0 };
|
|
66
|
+
}
|
|
67
|
+
if (this._httpEndpoint) {
|
|
68
|
+
const output = await this._execHttp(name, args);
|
|
69
|
+
return { result: output, source: 'http', elapsed: Date.now() - t0 };
|
|
70
|
+
}
|
|
71
|
+
throw Object.assign(new Error(`No handler found for tool: ${name}. Register with addHandler() or set httpEndpoint.`), { __qlnConfigError: true });
|
|
72
|
+
}
|
|
73
|
+
/** @internal Circuit breaker gate — throws if tool is disabled. */
|
|
74
|
+
_checkCircuit(name) {
|
|
75
|
+
const circuit = this._getCircuit(name);
|
|
76
|
+
if (circuit.state === 'open') {
|
|
77
|
+
const elapsed = Date.now() - circuit.lastFailure;
|
|
78
|
+
if (elapsed < this._cbConfig.recoveryTimeout) {
|
|
79
|
+
throw new Error(`[Circuit Breaker] ${name} is disabled (${circuit.failures} failures). ` +
|
|
80
|
+
`Recovery in ${Math.ceil((this._cbConfig.recoveryTimeout - elapsed) / 1000)}s.`);
|
|
81
|
+
}
|
|
82
|
+
circuit.state = 'half-open';
|
|
83
|
+
}
|
|
84
|
+
return circuit;
|
|
85
|
+
}
|
|
86
|
+
/** Get circuit breaker state for a tool. */
|
|
87
|
+
getCircuitState(name) {
|
|
88
|
+
const c = this._circuits.get(name);
|
|
89
|
+
if (!c)
|
|
90
|
+
return { state: 'closed', failures: 0 };
|
|
91
|
+
// Check for auto-recovery
|
|
92
|
+
if (c.state === 'open' && (Date.now() - c.lastFailure) >= this._cbConfig.recoveryTimeout) {
|
|
93
|
+
return { state: 'half-open', failures: c.failures };
|
|
94
|
+
}
|
|
95
|
+
return { state: c.state, failures: c.failures };
|
|
96
|
+
}
|
|
97
|
+
/** Manually reset circuit breaker for a tool. */
|
|
98
|
+
resetCircuit(name) {
|
|
99
|
+
this._circuits.delete(name);
|
|
100
|
+
}
|
|
101
|
+
/** Get all tripped circuits (for stats). */
|
|
102
|
+
getTrippedCircuits() {
|
|
103
|
+
const tripped = [];
|
|
104
|
+
for (const [name, entry] of this._circuits) {
|
|
105
|
+
if (entry.state !== 'closed') {
|
|
106
|
+
tripped.push({ name, failures: entry.failures, state: entry.state });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return tripped;
|
|
110
|
+
}
|
|
111
|
+
/** Dynamically set HTTP endpoint. */
|
|
112
|
+
setHttpEndpoint(endpoint) {
|
|
113
|
+
this._httpEndpoint = endpoint;
|
|
114
|
+
}
|
|
115
|
+
// ── Circuit Breaker internals ──
|
|
116
|
+
_getCircuit(name) {
|
|
117
|
+
if (!this._circuits.has(name)) {
|
|
118
|
+
this._circuits.set(name, { state: 'closed', failures: 0, lastFailure: 0, lastAttempt: 0 });
|
|
119
|
+
}
|
|
120
|
+
return this._circuits.get(name);
|
|
121
|
+
}
|
|
122
|
+
_recordSuccess(name) {
|
|
123
|
+
const circuit = this._getCircuit(name);
|
|
124
|
+
circuit.state = 'closed';
|
|
125
|
+
circuit.failures = 0;
|
|
126
|
+
}
|
|
127
|
+
_recordFailure(name) {
|
|
128
|
+
const circuit = this._getCircuit(name);
|
|
129
|
+
circuit.failures++;
|
|
130
|
+
circuit.lastFailure = Date.now();
|
|
131
|
+
if (circuit.failures >= this._cbConfig.failureThreshold) {
|
|
132
|
+
circuit.state = 'open';
|
|
133
|
+
console.error(`[QLN] Circuit Breaker: ${name} tripped (${circuit.failures} failures)`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** @internal HTTP POST /call → tool execution */
|
|
137
|
+
_execHttp(name, args) {
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const url = new URL(this._httpEndpoint);
|
|
140
|
+
const bodyStr = JSON.stringify({ tool: name, args });
|
|
141
|
+
const timer = setTimeout(() => reject(new Error(`timeout (${this._timeout}ms)`)), this._timeout);
|
|
142
|
+
const req = http_1.default.request({
|
|
143
|
+
hostname: url.hostname,
|
|
144
|
+
port: url.port,
|
|
145
|
+
path: '/call',
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: {
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
'Content-Length': Buffer.byteLength(bodyStr),
|
|
150
|
+
},
|
|
151
|
+
}, (res) => {
|
|
152
|
+
let body = '';
|
|
153
|
+
res.on('data', (c) => body += c);
|
|
154
|
+
res.on('end', () => {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(body);
|
|
158
|
+
if (parsed.error)
|
|
159
|
+
reject(new Error(parsed.error));
|
|
160
|
+
else
|
|
161
|
+
resolve(parsed.result);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
reject(new Error(`Invalid response: ${body.slice(0, 200)}`));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
req.on('error', (e) => { clearTimeout(timer); reject(e); });
|
|
169
|
+
req.write(bodyStr);
|
|
170
|
+
req.end();
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.Executor = Executor;
|
|
175
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/lib/executor.ts"],"names":[],"mappings":"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"}
|