@knolo/cli 3.2.1 → 3.2.2
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/bin/knolo.mjs +80 -1
- package/package.json +1 -1
package/bin/knolo.mjs
CHANGED
|
@@ -27,7 +27,7 @@ const DEFAULT_CONFIG = {
|
|
|
27
27
|
};
|
|
28
28
|
const SUPPORTED_EXTENSIONS = new Set(['.md', '.txt', '.json']);
|
|
29
29
|
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git']);
|
|
30
|
-
const SUBCOMMANDS = new Set(['init', 'add', 'build', 'query', 'dev']);
|
|
30
|
+
const SUBCOMMANDS = new Set(['init', 'add', 'build', 'query', 'dev', 'semantic:index', 'semantic:inspect', 'semantic:validate']);
|
|
31
31
|
|
|
32
32
|
function createError(message) {
|
|
33
33
|
return new Error(message);
|
|
@@ -87,6 +87,9 @@ function printCommandHelp(command) {
|
|
|
87
87
|
build: 'Usage: knolo build',
|
|
88
88
|
query: 'Usage: knolo query <question> [--pack <path>] [--k <number>] [--json]',
|
|
89
89
|
dev: 'Usage: knolo dev',
|
|
90
|
+
'semantic:index': 'Usage: knolo semantic:index --pack <path> [--out <path>] [--model <id>] [--endpoint <url>]',
|
|
91
|
+
'semantic:inspect': 'Usage: knolo semantic:inspect --sidecar <path>',
|
|
92
|
+
'semantic:validate': 'Usage: knolo semantic:validate --pack <path> --sidecar <path> --model <id>',
|
|
90
93
|
};
|
|
91
94
|
console.log(help[command] ?? 'Unknown command.');
|
|
92
95
|
}
|
|
@@ -313,6 +316,79 @@ async function cmdQuery(core, args) {
|
|
|
313
316
|
});
|
|
314
317
|
}
|
|
315
318
|
|
|
319
|
+
function parseKeyValueArgs(args) {
|
|
320
|
+
const out = {};
|
|
321
|
+
for (let i = 0; i < args.length; i++) {
|
|
322
|
+
const key = args[i];
|
|
323
|
+
if (!key.startsWith('--')) throw createError(`Unexpected argument: ${key}`);
|
|
324
|
+
out[key.slice(2)] = args[++i];
|
|
325
|
+
}
|
|
326
|
+
return out;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function loadOllamaProvider() {
|
|
330
|
+
const mod = await tryImport(path.resolve(__dirname, '../../semantic-ollama/dist/index.js'));
|
|
331
|
+
if (mod?.OllamaEmbeddingProvider) return mod.OllamaEmbeddingProvider;
|
|
332
|
+
const pkg = await tryImport('@knolo/semantic-ollama');
|
|
333
|
+
if (pkg?.OllamaEmbeddingProvider) return pkg.OllamaEmbeddingProvider;
|
|
334
|
+
throw createError('Could not load @knolo/semantic-ollama. Build packages/semantic-ollama first.');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function cmdSemanticIndex(core, args) {
|
|
338
|
+
const flags = parseKeyValueArgs(args);
|
|
339
|
+
const packPath = path.resolve(process.cwd(), flags.pack || 'dist/knowledge.knolo');
|
|
340
|
+
const outPath = path.resolve(process.cwd(), flags.out || `${packPath}.semantic.json`);
|
|
341
|
+
const modelId = flags.model || 'qwen3-embedding:4b';
|
|
342
|
+
const endpoint = flags.endpoint || 'http://localhost:11434';
|
|
343
|
+
if (!existsSync(packPath)) throw createError(`Pack file not found at ${path.relative(process.cwd(), packPath)}.`);
|
|
344
|
+
|
|
345
|
+
const bytes = Uint8Array.from(readFileSync(packPath));
|
|
346
|
+
const pack = await mountPackFromBytes(core, bytes);
|
|
347
|
+
const OllamaEmbeddingProvider = await loadOllamaProvider();
|
|
348
|
+
const provider = new OllamaEmbeddingProvider({ modelId, endpoint });
|
|
349
|
+
const vectors = await provider.embedTexts(pack.blocks);
|
|
350
|
+
const sidecar = {
|
|
351
|
+
version: 1,
|
|
352
|
+
packFingerprint: core.createPackFingerprint(pack),
|
|
353
|
+
modelId: provider.modelId,
|
|
354
|
+
dimension: vectors[0]?.length ?? 0,
|
|
355
|
+
metric: 'cosine',
|
|
356
|
+
createdAt: new Date().toISOString(),
|
|
357
|
+
blocks: vectors.map((vector, blockId) => ({ blockId, vector: Array.from(core.normalizeVector(vector)) })),
|
|
358
|
+
};
|
|
359
|
+
writeFileSync(outPath, core.serializeSidecar(sidecar));
|
|
360
|
+
console.log(`✔ wrote ${path.relative(process.cwd(), outPath)}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function cmdSemanticInspect(core, args) {
|
|
364
|
+
const flags = parseKeyValueArgs(args);
|
|
365
|
+
const sidecarPath = path.resolve(process.cwd(), flags.sidecar);
|
|
366
|
+
const sidecar = core.parseSidecar(readFileSync(sidecarPath, 'utf8'));
|
|
367
|
+
console.log(JSON.stringify({
|
|
368
|
+
version: sidecar.version,
|
|
369
|
+
packFingerprint: sidecar.packFingerprint,
|
|
370
|
+
modelId: sidecar.modelId,
|
|
371
|
+
dimension: sidecar.dimension,
|
|
372
|
+
metric: sidecar.metric,
|
|
373
|
+
createdAt: sidecar.createdAt,
|
|
374
|
+
blocks: sidecar.blocks.length,
|
|
375
|
+
}, null, 2));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function cmdSemanticValidate(core, args) {
|
|
379
|
+
const flags = parseKeyValueArgs(args);
|
|
380
|
+
const packPath = path.resolve(process.cwd(), flags.pack || 'dist/knowledge.knolo');
|
|
381
|
+
const sidecarPath = path.resolve(process.cwd(), flags.sidecar);
|
|
382
|
+
const modelId = flags.model;
|
|
383
|
+
if (!modelId) throw createError('semantic:validate requires --model <id>.');
|
|
384
|
+
const pack = await mountPackFromBytes(core, Uint8Array.from(readFileSync(packPath)));
|
|
385
|
+
const sidecar = core.parseSidecar(readFileSync(sidecarPath, 'utf8'));
|
|
386
|
+
core.validateSidecarForPack({ sidecar, pack, modelId });
|
|
387
|
+
if (sidecar.blocks.length !== pack.blocks.length) throw createError(`Semantic block count mismatch: sidecar=${sidecar.blocks.length}, pack=${pack.blocks.length}`);
|
|
388
|
+
if (sidecar.dimension <= 0) throw createError('Semantic sidecar dimension must be > 0.');
|
|
389
|
+
console.log('✔ semantic sidecar validation passed');
|
|
390
|
+
}
|
|
391
|
+
|
|
316
392
|
async function mountPackFromBytes(core, bytes) {
|
|
317
393
|
try {
|
|
318
394
|
return await core.mountPack({ bytes });
|
|
@@ -598,6 +674,9 @@ async function main() {
|
|
|
598
674
|
if (command === 'build') return await cmdBuild(core);
|
|
599
675
|
if (command === 'query') return await cmdQuery(core, commandArgs);
|
|
600
676
|
if (command === 'dev') return await cmdDev(core);
|
|
677
|
+
if (command === 'semantic:index') return await cmdSemanticIndex(core, commandArgs);
|
|
678
|
+
if (command === 'semantic:inspect') return await cmdSemanticInspect(core, commandArgs);
|
|
679
|
+
if (command === 'semantic:validate') return await cmdSemanticValidate(core, commandArgs);
|
|
601
680
|
}
|
|
602
681
|
|
|
603
682
|
if (command.startsWith('-')) throw createError(`Unknown option: ${command}`);
|