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
|
@@ -0,0 +1,207 @@
|
|
|
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.Store = void 0;
|
|
7
|
+
exports.initSqlJs = initSqlJs;
|
|
8
|
+
// QLN — SQLite storage engine (pure JS via sql.js WASM). No native dependencies.
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
/** sql.js module singleton */
|
|
12
|
+
let _SQL = null;
|
|
13
|
+
/** sql.js init promise */
|
|
14
|
+
let _initPromise = null;
|
|
15
|
+
/**
|
|
16
|
+
* Initialize sql.js WASM module (once per process).
|
|
17
|
+
*/
|
|
18
|
+
async function initSqlJs() {
|
|
19
|
+
if (_SQL)
|
|
20
|
+
return _SQL;
|
|
21
|
+
if (_initPromise)
|
|
22
|
+
return _initPromise;
|
|
23
|
+
_initPromise = (async () => {
|
|
24
|
+
// sql.js uses CJS export — require is needed here
|
|
25
|
+
const initFn = require('sql.js');
|
|
26
|
+
_SQL = await initFn();
|
|
27
|
+
return _SQL;
|
|
28
|
+
})();
|
|
29
|
+
return _initPromise;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* QLN SQLite store.
|
|
33
|
+
* Dedicated tool index DB — separated from Soul KV-Cache.
|
|
34
|
+
*
|
|
35
|
+
* File: {dataDir}/qln-tools.sqlite
|
|
36
|
+
*/
|
|
37
|
+
class Store {
|
|
38
|
+
_dataDir;
|
|
39
|
+
_db;
|
|
40
|
+
_dbPath;
|
|
41
|
+
constructor(dataDir) {
|
|
42
|
+
this._dataDir = dataDir;
|
|
43
|
+
this._db = null;
|
|
44
|
+
this._dbPath = path_1.default.join(dataDir, 'qln-tools.sqlite');
|
|
45
|
+
}
|
|
46
|
+
/** Async init — load sql.js + open DB + create schema */
|
|
47
|
+
async init() {
|
|
48
|
+
const SQL = await initSqlJs();
|
|
49
|
+
if (!fs_1.default.existsSync(this._dataDir)) {
|
|
50
|
+
fs_1.default.mkdirSync(this._dataDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
if (fs_1.default.existsSync(this._dbPath)) {
|
|
53
|
+
const buffer = fs_1.default.readFileSync(this._dbPath);
|
|
54
|
+
this._db = new SQL.Database(buffer);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this._db = new SQL.Database();
|
|
58
|
+
}
|
|
59
|
+
this._createSchema();
|
|
60
|
+
}
|
|
61
|
+
/** Create tools + providers table schema */
|
|
62
|
+
_createSchema() {
|
|
63
|
+
this._db.run(`
|
|
64
|
+
CREATE TABLE IF NOT EXISTS tools (
|
|
65
|
+
name TEXT PRIMARY KEY,
|
|
66
|
+
description TEXT DEFAULT '',
|
|
67
|
+
source TEXT DEFAULT 'unknown',
|
|
68
|
+
category TEXT DEFAULT 'misc',
|
|
69
|
+
provider TEXT DEFAULT '',
|
|
70
|
+
input_schema TEXT DEFAULT '{}',
|
|
71
|
+
triggers TEXT DEFAULT '[]',
|
|
72
|
+
tags TEXT DEFAULT '[]',
|
|
73
|
+
examples TEXT DEFAULT '[]',
|
|
74
|
+
endpoint TEXT DEFAULT '',
|
|
75
|
+
search_text TEXT DEFAULT '',
|
|
76
|
+
embedding TEXT DEFAULT '',
|
|
77
|
+
usage_count INTEGER DEFAULT 0,
|
|
78
|
+
success_rate REAL DEFAULT 1.0,
|
|
79
|
+
registered_at TEXT DEFAULT (datetime('now')),
|
|
80
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
81
|
+
)
|
|
82
|
+
`);
|
|
83
|
+
this._db.run(`CREATE INDEX IF NOT EXISTS idx_tools_source ON tools(source)`);
|
|
84
|
+
this._db.run(`CREATE INDEX IF NOT EXISTS idx_tools_category ON tools(category)`);
|
|
85
|
+
this._db.run(`
|
|
86
|
+
CREATE TABLE IF NOT EXISTS providers (
|
|
87
|
+
name TEXT PRIMARY KEY,
|
|
88
|
+
version TEXT DEFAULT '1.0.0',
|
|
89
|
+
description TEXT DEFAULT '',
|
|
90
|
+
endpoint TEXT DEFAULT '',
|
|
91
|
+
tool_count INTEGER DEFAULT 0,
|
|
92
|
+
registered_at TEXT DEFAULT (datetime('now')),
|
|
93
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
94
|
+
)
|
|
95
|
+
`);
|
|
96
|
+
this._migrateSchema();
|
|
97
|
+
}
|
|
98
|
+
/** Schema migration — safe ADD COLUMN (ignores if already exists) */
|
|
99
|
+
_migrateSchema() {
|
|
100
|
+
const addCol = (table, col, type, dflt) => {
|
|
101
|
+
try {
|
|
102
|
+
this._db.run(`ALTER TABLE ${table} ADD COLUMN ${col} ${type} DEFAULT ${dflt}`);
|
|
103
|
+
}
|
|
104
|
+
catch { /* column already exists */ }
|
|
105
|
+
};
|
|
106
|
+
addCol('tools', 'provider', 'TEXT', "''");
|
|
107
|
+
addCol('tools', 'examples', 'TEXT', "'[]'");
|
|
108
|
+
addCol('tools', 'endpoint', 'TEXT', "''");
|
|
109
|
+
addCol('tools', 'last_used_at', 'TEXT', 'NULL');
|
|
110
|
+
// v4.1.0: boostKeywords + Circuit Breaker
|
|
111
|
+
addCol('tools', 'boost_keywords', 'TEXT', "''");
|
|
112
|
+
addCol('tools', 'consecutive_failures', 'INTEGER', '0');
|
|
113
|
+
// Copy plugin_name → provider (if old schema has plugin_name)
|
|
114
|
+
try {
|
|
115
|
+
const cols = this._db.exec("PRAGMA table_info(tools)");
|
|
116
|
+
if (cols.length > 0) {
|
|
117
|
+
const colNames = cols[0].values.map(r => r[1]);
|
|
118
|
+
if (colNames.includes('plugin_name') && colNames.includes('provider')) {
|
|
119
|
+
this._db.run("UPDATE tools SET provider = plugin_name WHERE provider = '' AND plugin_name != ''");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch { /* table doesn't exist yet */ }
|
|
124
|
+
try {
|
|
125
|
+
this._db.run(`CREATE INDEX IF NOT EXISTS idx_tools_provider ON tools(provider)`);
|
|
126
|
+
}
|
|
127
|
+
catch { /* already exists */ }
|
|
128
|
+
}
|
|
129
|
+
/** Upsert a tool entry. */
|
|
130
|
+
upsert(entry) {
|
|
131
|
+
this._db.run(`
|
|
132
|
+
INSERT INTO tools (name, description, source, category, provider,
|
|
133
|
+
input_schema, triggers, tags, examples, endpoint, search_text, boost_keywords,
|
|
134
|
+
embedding, usage_count, success_rate, consecutive_failures, last_used_at, updated_at)
|
|
135
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
136
|
+
?, ?, COALESCE(?, (SELECT embedding FROM tools WHERE name = ?)),
|
|
137
|
+
?, ?, ?, ?, datetime('now'))
|
|
138
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
139
|
+
description = excluded.description,
|
|
140
|
+
source = excluded.source,
|
|
141
|
+
category = excluded.category,
|
|
142
|
+
provider = excluded.provider,
|
|
143
|
+
input_schema = excluded.input_schema,
|
|
144
|
+
triggers = excluded.triggers,
|
|
145
|
+
tags = excluded.tags,
|
|
146
|
+
examples = excluded.examples,
|
|
147
|
+
endpoint = excluded.endpoint,
|
|
148
|
+
search_text = excluded.search_text,
|
|
149
|
+
boost_keywords = excluded.boost_keywords,
|
|
150
|
+
embedding = COALESCE(excluded.embedding, tools.embedding),
|
|
151
|
+
usage_count = excluded.usage_count,
|
|
152
|
+
success_rate = excluded.success_rate,
|
|
153
|
+
consecutive_failures = excluded.consecutive_failures,
|
|
154
|
+
last_used_at = excluded.last_used_at,
|
|
155
|
+
updated_at = excluded.updated_at
|
|
156
|
+
`, [
|
|
157
|
+
entry.name, entry.description, entry.source, entry.category, entry.provider,
|
|
158
|
+
JSON.stringify(entry.inputSchema), JSON.stringify(entry.triggers),
|
|
159
|
+
JSON.stringify(entry.tags), JSON.stringify(entry.examples), entry.endpoint || '',
|
|
160
|
+
entry.searchText, entry.boostKeywords || '',
|
|
161
|
+
entry.embedding ? JSON.stringify(entry.embedding) : null, entry.name,
|
|
162
|
+
entry.usageCount, entry.successRate, entry.consecutiveFailures || 0,
|
|
163
|
+
entry.lastUsedAt || null,
|
|
164
|
+
]);
|
|
165
|
+
this._persist();
|
|
166
|
+
}
|
|
167
|
+
/** Remove a tool by name. */
|
|
168
|
+
remove(name) {
|
|
169
|
+
this._db.run('DELETE FROM tools WHERE name = ?', [name]);
|
|
170
|
+
this._persist();
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
/** Purge all tools by source (for re-sync). */
|
|
174
|
+
purgeBySource(source) {
|
|
175
|
+
this._db.run('DELETE FROM tools WHERE source = ?', [source]);
|
|
176
|
+
this._persist();
|
|
177
|
+
}
|
|
178
|
+
/** Load all tools from DB. */
|
|
179
|
+
loadAll() {
|
|
180
|
+
const results = this._db.exec('SELECT * FROM tools ORDER BY name');
|
|
181
|
+
if (results.length === 0)
|
|
182
|
+
return [];
|
|
183
|
+
const cols = results[0].columns;
|
|
184
|
+
return results[0].values.map(row => {
|
|
185
|
+
const obj = {};
|
|
186
|
+
cols.forEach((c, i) => { obj[c] = row[i]; });
|
|
187
|
+
return obj;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/** Persist DB to disk */
|
|
191
|
+
_persist() {
|
|
192
|
+
if (!this._db)
|
|
193
|
+
return;
|
|
194
|
+
const data = this._db.export();
|
|
195
|
+
fs_1.default.writeFileSync(this._dbPath, Buffer.from(data));
|
|
196
|
+
}
|
|
197
|
+
/** Close connection */
|
|
198
|
+
dispose() {
|
|
199
|
+
if (this._db) {
|
|
200
|
+
this._persist();
|
|
201
|
+
this._db.close();
|
|
202
|
+
this._db = null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.Store = Store;
|
|
207
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ValidationError } from '../types';
|
|
2
|
+
/** Valid tool categories */
|
|
3
|
+
export declare const VALID_CATEGORIES: readonly ["web", "data", "file", "dev", "ai", "capture", "misc"];
|
|
4
|
+
/** Tool name pattern: verb_target (e.g. read_pdf, take_screenshot) */
|
|
5
|
+
export declare const NAME_PATTERN: RegExp;
|
|
6
|
+
/** Minimum description length */
|
|
7
|
+
export declare const MIN_DESCRIPTION_LENGTH = 10;
|
|
8
|
+
export interface ValidatableParams {
|
|
9
|
+
name?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
category?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
interface RegistryLike {
|
|
16
|
+
get(name: string): unknown | null;
|
|
17
|
+
}
|
|
18
|
+
interface ValidationResult {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
errors: ValidationError[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validate tool entry for registration.
|
|
24
|
+
* Pattern: accumulate errors → reject if any error exists.
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateToolEntry(params: ValidatableParams, registry: RegistryLike): ValidationResult;
|
|
27
|
+
/**
|
|
28
|
+
* Validate tool update params.
|
|
29
|
+
* Same rules as create, but no duplicate check (updating existing tool).
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateUpdateEntry(params: ValidatableParams, existing: ValidatableParams): ValidationResult;
|
|
32
|
+
/**
|
|
33
|
+
* Format validation errors as user-readable string.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatValidationErrors(errors: ValidationError[]): string;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MIN_DESCRIPTION_LENGTH = exports.NAME_PATTERN = exports.VALID_CATEGORIES = void 0;
|
|
4
|
+
exports.validateToolEntry = validateToolEntry;
|
|
5
|
+
exports.validateUpdateEntry = validateUpdateEntry;
|
|
6
|
+
exports.formatValidationErrors = formatValidationErrors;
|
|
7
|
+
/** Valid tool categories */
|
|
8
|
+
exports.VALID_CATEGORIES = ['web', 'data', 'file', 'dev', 'ai', 'capture', 'misc'];
|
|
9
|
+
/** Tool name pattern: verb_target (e.g. read_pdf, take_screenshot) */
|
|
10
|
+
exports.NAME_PATTERN = /^[a-z]+_[a-z][a-z0-9_]*$/;
|
|
11
|
+
/** Minimum description length */
|
|
12
|
+
exports.MIN_DESCRIPTION_LENGTH = 10;
|
|
13
|
+
/** Validate name: required + verb_target format */
|
|
14
|
+
function _validateName(name, errors) {
|
|
15
|
+
if (!name || name.trim() === '') {
|
|
16
|
+
errors.push({ field: 'name', message: 'name은 필수입니다', severity: 'error' });
|
|
17
|
+
}
|
|
18
|
+
else if (!exports.NAME_PATTERN.test(name)) {
|
|
19
|
+
errors.push({ field: 'name', message: `'${name}' → 동사_대상 형식 필수 (예: read_pdf, take_screenshot)`, severity: 'error' });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Validate description: required + min length */
|
|
23
|
+
function _validateDescription(desc, errors) {
|
|
24
|
+
if (!desc || desc.trim() === '') {
|
|
25
|
+
errors.push({ field: 'description', message: 'description은 필수입니다 (도구 용도 설명)', severity: 'error' });
|
|
26
|
+
}
|
|
27
|
+
else if (desc.trim().length < exports.MIN_DESCRIPTION_LENGTH) {
|
|
28
|
+
errors.push({ field: 'description', message: `최소 ${exports.MIN_DESCRIPTION_LENGTH}자 이상 설명 필요 (현재: ${desc.trim().length}자)`, severity: 'error' });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Validate category: must be in enum */
|
|
32
|
+
function _validateCategory(category, errors) {
|
|
33
|
+
if (category && !exports.VALID_CATEGORIES.includes(category)) {
|
|
34
|
+
errors.push({ field: 'category', message: `'${category}' → ${exports.VALID_CATEGORIES.join('|')} 중 하나`, severity: 'error' });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate tool entry for registration.
|
|
39
|
+
* Pattern: accumulate errors → reject if any error exists.
|
|
40
|
+
*/
|
|
41
|
+
function validateToolEntry(params, registry) {
|
|
42
|
+
const errors = [];
|
|
43
|
+
_validateName(params.name, errors);
|
|
44
|
+
_validateDescription(params.description, errors);
|
|
45
|
+
_validateCategory(params.category, errors);
|
|
46
|
+
// Rule 4: duplicate name check
|
|
47
|
+
if (params.name && registry && registry.get(params.name)) {
|
|
48
|
+
errors.push({ field: 'name', message: `'${params.name}' 이미 존재합니다. action: "update" 를 사용하세요`, severity: 'error' });
|
|
49
|
+
}
|
|
50
|
+
// Warning: no tags (search accuracy may be lower)
|
|
51
|
+
if (!params.tags || params.tags.length === 0) {
|
|
52
|
+
errors.push({ field: 'tags', message: 'tags 미지정 — 검색 정확도가 낮아질 수 있습니다', severity: 'warning' });
|
|
53
|
+
}
|
|
54
|
+
const errorCount = errors.filter(e => e.severity === 'error').length;
|
|
55
|
+
return { valid: errorCount === 0, errors };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate tool update params.
|
|
59
|
+
* Same rules as create, but no duplicate check (updating existing tool).
|
|
60
|
+
*/
|
|
61
|
+
function validateUpdateEntry(params, existing) {
|
|
62
|
+
const errors = [];
|
|
63
|
+
// Rule 1: name format (if changed)
|
|
64
|
+
if (params.name && !exports.NAME_PATTERN.test(params.name)) {
|
|
65
|
+
errors.push({
|
|
66
|
+
field: 'name',
|
|
67
|
+
message: `'${params.name}' → 동사_대상 형식 필수 (예: read_pdf, take_screenshot)`,
|
|
68
|
+
severity: 'error',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Rule 2: description min length (if changed)
|
|
72
|
+
if (params.description && params.description.trim().length < exports.MIN_DESCRIPTION_LENGTH) {
|
|
73
|
+
errors.push({
|
|
74
|
+
field: 'description',
|
|
75
|
+
message: `최소 ${exports.MIN_DESCRIPTION_LENGTH}자 이상 설명 필요 (현재: ${params.description.trim().length}자)`,
|
|
76
|
+
severity: 'error',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Rule 2b: warn if description is shorter than existing
|
|
80
|
+
if (params.description && existing.description
|
|
81
|
+
&& params.description.trim().length < existing.description.trim().length * 0.5) {
|
|
82
|
+
errors.push({
|
|
83
|
+
field: 'description',
|
|
84
|
+
message: `설명이 기존보다 50% 이상 짧아집니다 (${existing.description.trim().length}자 → ${params.description.trim().length}자)`,
|
|
85
|
+
severity: 'warning',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Rule 3: category enum (if changed)
|
|
89
|
+
if (params.category && !exports.VALID_CATEGORIES.includes(params.category)) {
|
|
90
|
+
errors.push({
|
|
91
|
+
field: 'category',
|
|
92
|
+
message: `'${params.category}' → ${exports.VALID_CATEGORIES.join('|')} 중 하나`,
|
|
93
|
+
severity: 'error',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const errorCount = errors.filter(e => e.severity === 'error').length;
|
|
97
|
+
return { valid: errorCount === 0, errors };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Format validation errors as user-readable string.
|
|
101
|
+
*/
|
|
102
|
+
function formatValidationErrors(errors) {
|
|
103
|
+
const errorCount = errors.filter(e => e.severity === 'error').length;
|
|
104
|
+
const warnCount = errors.filter(e => e.severity === 'warning').length;
|
|
105
|
+
const lines = errors.map(e => {
|
|
106
|
+
const icon = e.severity === 'error' ? '❌' : '⚠️';
|
|
107
|
+
return ` ${icon} [${e.field}] ${e.message}`;
|
|
108
|
+
});
|
|
109
|
+
const header = errorCount > 0
|
|
110
|
+
? `등록 거부 (${errorCount} error${errorCount > 1 ? 's' : ''}${warnCount > 0 ? `, ${warnCount} warning` : ''}):`
|
|
111
|
+
: `등록 완료 (${warnCount} warning${warnCount > 1 ? 's' : ''}):`;
|
|
112
|
+
return `${header}\n${lines.join('\n')}`;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/lib/validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ToolEntry, VectorBuildResult, VectorStats } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Vector index — Float32Array matrix + category centroid partitioned search.
|
|
4
|
+
*
|
|
5
|
+
* Scaling:
|
|
6
|
+
* 100 tools → ~1ms
|
|
7
|
+
* 1,000 tools → ~3ms
|
|
8
|
+
* 10,000 tools → ~5ms (centroid hierarchy)
|
|
9
|
+
*/
|
|
10
|
+
export declare class VectorIndex {
|
|
11
|
+
private _matrix;
|
|
12
|
+
private _names;
|
|
13
|
+
private _dim;
|
|
14
|
+
private _count;
|
|
15
|
+
private _centroids;
|
|
16
|
+
private _partitions;
|
|
17
|
+
private _built;
|
|
18
|
+
constructor();
|
|
19
|
+
/**
|
|
20
|
+
* Build vector index from tool entries.
|
|
21
|
+
* Only indexes tools with embeddings, computes per-category centroids.
|
|
22
|
+
*/
|
|
23
|
+
build(tools: ToolEntry[]): VectorBuildResult;
|
|
24
|
+
/**
|
|
25
|
+
* Centroid hierarchy search — top-K categories → scan within partition.
|
|
26
|
+
*/
|
|
27
|
+
search(queryVec: number[], topK?: number, topCategories?: number): Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
score: number;
|
|
30
|
+
}>;
|
|
31
|
+
stats(): VectorStats;
|
|
32
|
+
/** Reset index */
|
|
33
|
+
reset(): void;
|
|
34
|
+
/** Cosine similarity */
|
|
35
|
+
private _cosineSim;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=vector-index.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VectorIndex = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Vector index — Float32Array matrix + category centroid partitioned search.
|
|
6
6
|
*
|
|
@@ -10,27 +10,25 @@
|
|
|
10
10
|
* 10,000 tools → ~5ms (centroid hierarchy)
|
|
11
11
|
*/
|
|
12
12
|
class VectorIndex {
|
|
13
|
+
_matrix;
|
|
14
|
+
_names;
|
|
15
|
+
_dim;
|
|
16
|
+
_count;
|
|
17
|
+
_centroids;
|
|
18
|
+
_partitions;
|
|
19
|
+
_built;
|
|
13
20
|
constructor() {
|
|
14
|
-
/** @type {Float32Array|null} N tools × D dimensions */
|
|
15
21
|
this._matrix = null;
|
|
16
|
-
/** @type {string[]} Tool names (matrix row order) */
|
|
17
22
|
this._names = [];
|
|
18
|
-
/** @type {number} Embedding dimension */
|
|
19
23
|
this._dim = 0;
|
|
20
|
-
/** @type {number} Number of indexed tools */
|
|
21
24
|
this._count = 0;
|
|
22
|
-
/** @type {Map<string, Float32Array>} Category → centroid vector */
|
|
23
25
|
this._centroids = new Map();
|
|
24
|
-
/** @type {Map<string, number[]>} Category → matrix row indices */
|
|
25
26
|
this._partitions = new Map();
|
|
26
27
|
this._built = false;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
29
|
/**
|
|
30
30
|
* Build vector index from tool entries.
|
|
31
31
|
* Only indexes tools with embeddings, computes per-category centroids.
|
|
32
|
-
* @param {object[]} tools
|
|
33
|
-
* @returns {{indexed: number, categories: number, dimension: number}}
|
|
34
32
|
*/
|
|
35
33
|
build(tools) {
|
|
36
34
|
const valid = tools.filter(t => t.embedding && Array.isArray(t.embedding) && t.embedding.length > 0);
|
|
@@ -38,11 +36,9 @@ class VectorIndex {
|
|
|
38
36
|
this._built = false;
|
|
39
37
|
return { indexed: 0, categories: 0, dimension: 0 };
|
|
40
38
|
}
|
|
41
|
-
|
|
42
39
|
this._dim = valid[0].embedding.length;
|
|
43
40
|
this._count = valid.length;
|
|
44
41
|
this._names = valid.map(t => t.name);
|
|
45
|
-
|
|
46
42
|
// Pack into Float32Array matrix
|
|
47
43
|
this._matrix = new Float32Array(this._count * this._dim);
|
|
48
44
|
for (let i = 0; i < this._count; i++) {
|
|
@@ -51,16 +47,15 @@ class VectorIndex {
|
|
|
51
47
|
this._matrix[i * this._dim + j] = vec[j] || 0;
|
|
52
48
|
}
|
|
53
49
|
}
|
|
54
|
-
|
|
55
50
|
// Build category partitions
|
|
56
51
|
this._partitions.clear();
|
|
57
52
|
this._centroids.clear();
|
|
58
53
|
for (let i = 0; i < valid.length; i++) {
|
|
59
54
|
const cat = valid[i].category || 'misc';
|
|
60
|
-
if (!this._partitions.has(cat))
|
|
55
|
+
if (!this._partitions.has(cat))
|
|
56
|
+
this._partitions.set(cat, []);
|
|
61
57
|
this._partitions.get(cat).push(i);
|
|
62
58
|
}
|
|
63
|
-
|
|
64
59
|
// Compute centroids (category average vector)
|
|
65
60
|
for (const [cat, indices] of this._partitions) {
|
|
66
61
|
const centroid = new Float32Array(this._dim);
|
|
@@ -71,26 +66,20 @@ class VectorIndex {
|
|
|
71
66
|
}
|
|
72
67
|
}
|
|
73
68
|
const n = indices.length;
|
|
74
|
-
for (let j = 0; j < this._dim; j++)
|
|
69
|
+
for (let j = 0; j < this._dim; j++)
|
|
70
|
+
centroid[j] /= n;
|
|
75
71
|
this._centroids.set(cat, centroid);
|
|
76
72
|
}
|
|
77
|
-
|
|
78
73
|
this._built = true;
|
|
79
74
|
return { indexed: this._count, categories: this._partitions.size, dimension: this._dim };
|
|
80
75
|
}
|
|
81
|
-
|
|
82
76
|
/**
|
|
83
77
|
* Centroid hierarchy search — top-K categories → scan within partition.
|
|
84
|
-
* @param {number[]} queryVec
|
|
85
|
-
* @param {number} topK
|
|
86
|
-
* @param {number} topCategories
|
|
87
|
-
* @returns {{name: string, score: number}[]}
|
|
88
78
|
*/
|
|
89
79
|
search(queryVec, topK = 10, topCategories = 4) {
|
|
90
|
-
if (!this._built || !this._matrix || !queryVec || queryVec.length !== this._dim)
|
|
91
|
-
|
|
80
|
+
if (!this._built || !this._matrix || !queryVec || queryVec.length !== this._dim)
|
|
81
|
+
return [];
|
|
92
82
|
const qVec = new Float32Array(queryVec);
|
|
93
|
-
|
|
94
83
|
// Step 1: Rank categories by centroid similarity
|
|
95
84
|
const catScores = [];
|
|
96
85
|
for (const [cat, centroid] of this._centroids) {
|
|
@@ -98,7 +87,6 @@ class VectorIndex {
|
|
|
98
87
|
}
|
|
99
88
|
catScores.sort((a, b) => b.score - a.score);
|
|
100
89
|
const selectedCats = catScores.slice(0, topCategories).map(c => c.cat);
|
|
101
|
-
|
|
102
90
|
// Step 2: Search individual tools within selected partitions
|
|
103
91
|
const candidates = [];
|
|
104
92
|
for (const cat of selectedCats) {
|
|
@@ -107,12 +95,9 @@ class VectorIndex {
|
|
|
107
95
|
candidates.push({ name: this._names[idx], score: this._cosineSim(qVec, toolVec) });
|
|
108
96
|
}
|
|
109
97
|
}
|
|
110
|
-
|
|
111
98
|
candidates.sort((a, b) => b.score - a.score);
|
|
112
99
|
return candidates.slice(0, topK);
|
|
113
100
|
}
|
|
114
|
-
|
|
115
|
-
/** @returns {object} */
|
|
116
101
|
stats() {
|
|
117
102
|
return {
|
|
118
103
|
built: this._built,
|
|
@@ -123,7 +108,6 @@ class VectorIndex {
|
|
|
123
108
|
memoryKB: this._matrix ? Math.round(this._matrix.byteLength / 1024) : 0,
|
|
124
109
|
};
|
|
125
110
|
}
|
|
126
|
-
|
|
127
111
|
/** Reset index */
|
|
128
112
|
reset() {
|
|
129
113
|
this._matrix = null;
|
|
@@ -134,8 +118,7 @@ class VectorIndex {
|
|
|
134
118
|
this._partitions.clear();
|
|
135
119
|
this._built = false;
|
|
136
120
|
}
|
|
137
|
-
|
|
138
|
-
/** @private Cosine similarity */
|
|
121
|
+
/** Cosine similarity */
|
|
139
122
|
_cosineSim(a, b) {
|
|
140
123
|
let dot = 0, normA = 0, normB = 0;
|
|
141
124
|
for (let i = 0; i < a.length; i++) {
|
|
@@ -147,5 +130,5 @@ class VectorIndex {
|
|
|
147
130
|
return denom === 0 ? 0 : dot / denom;
|
|
148
131
|
}
|
|
149
132
|
}
|
|
150
|
-
|
|
151
|
-
|
|
133
|
+
exports.VectorIndex = VectorIndex;
|
|
134
|
+
//# sourceMappingURL=vector-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-index.js","sourceRoot":"","sources":["../../src/lib/vector-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Router } from '../lib/router';
|
|
2
|
+
import type { Executor } from '../lib/executor';
|
|
3
|
+
import type { Registry } from '../lib/registry';
|
|
4
|
+
import type { McpDiscovery } from '../lib/mcp-discovery';
|
|
5
|
+
/** Zod-like validator interface (minimal) */
|
|
6
|
+
interface ZodLike {
|
|
7
|
+
enum(values: readonly string[]): {
|
|
8
|
+
describe(d: string): unknown;
|
|
9
|
+
};
|
|
10
|
+
string(): {
|
|
11
|
+
optional(): {
|
|
12
|
+
describe(d: string): unknown;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
number(): {
|
|
16
|
+
optional(): {
|
|
17
|
+
describe(d: string): unknown;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
record(v: unknown): {
|
|
21
|
+
optional(): {
|
|
22
|
+
describe(d: string): unknown;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
array(v: unknown): {
|
|
26
|
+
optional(): {
|
|
27
|
+
describe(d: string): unknown;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
unknown(): unknown;
|
|
31
|
+
}
|
|
32
|
+
/** MCP server interface (minimal — uses any to match SDK's complex overloads) */
|
|
33
|
+
interface McpServerLike {
|
|
34
|
+
tool(...args: unknown[]): unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Register the unified n2_qln_call MCP tool.
|
|
38
|
+
*/
|
|
39
|
+
export declare function registerQlnCall(server: McpServerLike, z: ZodLike, router: Router, executor: Executor, registry: Registry, discovery?: McpDiscovery | null): void;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=qln-call.d.ts.map
|