@kaelio/ktx 0.3.0 → 0.4.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.
Files changed (52) hide show
  1. package/assets/python/{kaelio_ktx-0.3.0-py3-none-any.whl → kaelio_ktx-0.4.1-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/admin-reindex.js +9 -14
  4. package/dist/admin-reindex.test.js +4 -1
  5. package/dist/cli-project.d.ts +4 -10
  6. package/dist/cli-project.js +5 -49
  7. package/dist/cli-project.test.js +4 -131
  8. package/dist/commands/knowledge-commands.js +2 -0
  9. package/dist/commands/sl-commands.js +2 -0
  10. package/dist/embedding-resolution.d.ts +36 -0
  11. package/dist/embedding-resolution.js +63 -0
  12. package/dist/embedding-resolution.test.d.ts +1 -0
  13. package/dist/embedding-resolution.test.js +132 -0
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.test.js +36 -33
  17. package/dist/ingest.js +12 -9
  18. package/dist/knowledge.d.ts +7 -2
  19. package/dist/knowledge.js +21 -7
  20. package/dist/knowledge.test.js +53 -9
  21. package/dist/managed-local-embeddings.d.ts +7 -6
  22. package/dist/managed-local-embeddings.js +19 -13
  23. package/dist/managed-local-embeddings.test.js +87 -18
  24. package/dist/mcp-server-factory.js +11 -0
  25. package/dist/public-ingest.js +2 -8
  26. package/dist/runtime-requirements.js +1 -2
  27. package/dist/runtime-requirements.test.js +1 -2
  28. package/dist/scan.js +8 -4
  29. package/dist/setup-embeddings.js +6 -2
  30. package/dist/setup-embeddings.test.js +2 -5
  31. package/dist/setup-runtime.test.js +1 -3
  32. package/dist/sl.d.ts +6 -4
  33. package/dist/sl.js +23 -9
  34. package/dist/sl.test.js +77 -6
  35. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.d.ts +2 -0
  36. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +2 -2
  37. package/node_modules/@ktx/context/dist/ingest/local-ingest.d.ts +1 -0
  38. package/node_modules/@ktx/context/dist/ingest/local-ingest.js +2 -0
  39. package/node_modules/@ktx/context/dist/llm/index.d.ts +1 -1
  40. package/node_modules/@ktx/context/dist/llm/index.js +1 -1
  41. package/node_modules/@ktx/context/dist/llm/local-config.d.ts +0 -1
  42. package/node_modules/@ktx/context/dist/llm/local-config.js +1 -2
  43. package/node_modules/@ktx/context/dist/llm/local-config.test.js +3 -3
  44. package/node_modules/@ktx/context/dist/mcp/local-project-ports.d.ts +2 -2
  45. package/node_modules/@ktx/context/dist/mcp/local-project-ports.js +2 -5
  46. package/node_modules/@ktx/context/dist/mcp/local-project-ports.test.js +14 -10
  47. package/node_modules/@ktx/context/dist/package-exports.test.js +0 -1
  48. package/node_modules/@ktx/context/dist/project/config.js +1 -1
  49. package/node_modules/@ktx/context/dist/scan/local-enrichment.test.js +4 -5
  50. package/node_modules/@ktx/context/dist/scan/local-scan.d.ts +3 -1
  51. package/node_modules/@ktx/context/dist/scan/local-scan.js +3 -2
  52. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import { buildDefaultKtxProjectConfig, } from '../project/config.js';
3
- import { MANAGED_SENTENCE_TRANSFORMERS_BASE_URL, createLocalKtxEmbeddingProviderFromConfig, createLocalKtxLlmProviderFromConfig, resolveLocalKtxEmbeddingConfig, resolveLocalKtxLlmConfig, } from './local-config.js';
3
+ import { createLocalKtxEmbeddingProviderFromConfig, createLocalKtxLlmProviderFromConfig, resolveLocalKtxEmbeddingConfig, resolveLocalKtxLlmConfig, } from './local-config.js';
4
4
  describe('local KTX LLM config', () => {
5
5
  it('resolves env and file references into a KtxLlmConfig', () => {
6
6
  const config = {
@@ -116,13 +116,13 @@ describe('local KTX embedding config', () => {
116
116
  batchSize: 16,
117
117
  });
118
118
  });
119
- it('returns null when sentence-transformers base_url is still the unresolved managed sentinel', () => {
119
+ it('returns null when sentence-transformers has no base_url (managed daemon delegation)', () => {
120
120
  const config = {
121
121
  backend: 'sentence-transformers',
122
122
  model: 'all-MiniLM-L6-v2',
123
123
  dimensions: 384,
124
124
  sentenceTransformers: {
125
- base_url: MANAGED_SENTENCE_TRANSFORMERS_BASE_URL,
125
+ base_url: '',
126
126
  pathPrefix: '',
127
127
  },
128
128
  };
@@ -10,7 +10,7 @@ interface CreateLocalProjectMcpContextPortsOptions {
10
10
  queryExecutor?: KtxSqlQueryExecutorPort;
11
11
  sqlAnalysis?: SqlAnalysisPort;
12
12
  localScan?: LocalScanMcpOptions;
13
- embeddingService?: KtxEmbeddingPort | null;
13
+ embeddingService: KtxEmbeddingPort | null;
14
14
  }
15
- export declare function createLocalProjectMcpContextPorts(project: KtxLocalProject, options?: CreateLocalProjectMcpContextPortsOptions): KtxMcpContextPorts;
15
+ export declare function createLocalProjectMcpContextPorts(project: KtxLocalProject, options: CreateLocalProjectMcpContextPortsOptions): KtxMcpContextPorts;
16
16
  export {};
@@ -1,5 +1,4 @@
1
1
  import { localConnectionInfoFromConfig } from '../connections/index.js';
2
- import { createLocalKtxEmbeddingProviderFromConfig, KtxIngestEmbeddingPortAdapter } from '../llm/index.js';
3
2
  import { createKtxEntityDetailsService } from '../scan/index.js';
4
3
  import { createKtxDiscoverDataService } from '../search/index.js';
5
4
  import { compileLocalSlQuery, createKtxDictionarySearchService } from '../sl/index.js';
@@ -99,10 +98,8 @@ async function executeValidatedReadOnlySql(project, options, input, onProgress)
99
98
  await cleanupConnector(connector);
100
99
  }
101
100
  }
102
- export function createLocalProjectMcpContextPorts(project, options = {}) {
103
- const configuredEmbeddingProvider = createLocalKtxEmbeddingProviderFromConfig(project.config.ingest.embeddings);
104
- const embeddingService = options.embeddingService ??
105
- (configuredEmbeddingProvider ? new KtxIngestEmbeddingPortAdapter(configuredEmbeddingProvider) : null);
101
+ export function createLocalProjectMcpContextPorts(project, options) {
102
+ const embeddingService = options.embeddingService;
106
103
  const ports = {
107
104
  connections: {
108
105
  async list() {
@@ -138,7 +138,7 @@ describe('createLocalProjectMcpContextPorts', () => {
138
138
  driver: 'postgres',
139
139
  url: 'env:DATABASE_URL',
140
140
  };
141
- const ports = createLocalProjectMcpContextPorts(project);
141
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
142
142
  expect(Object.keys(ports).sort()).toEqual([
143
143
  'connections',
144
144
  'dictionarySearch',
@@ -178,6 +178,7 @@ describe('createLocalProjectMcpContextPorts', () => {
178
178
  localScan: {
179
179
  createConnector,
180
180
  },
181
+ embeddingService: null,
181
182
  });
182
183
  expect(Object.keys(ports).sort()).toContain('sqlExecution');
183
184
  await expect(ports.sqlExecution?.execute({
@@ -224,6 +225,7 @@ describe('createLocalProjectMcpContextPorts', () => {
224
225
  localScan: {
225
226
  createConnector,
226
227
  },
228
+ embeddingService: null,
227
229
  });
228
230
  const result = await ports.sqlExecution?.execute({ connectionId: 'warehouse', sql: 'select id from public.orders', maxRows: 5 }, {
229
231
  onProgress: (event) => {
@@ -262,6 +264,7 @@ describe('createLocalProjectMcpContextPorts', () => {
262
264
  localScan: {
263
265
  createConnector: vi.fn(async () => connector),
264
266
  },
267
+ embeddingService: null,
265
268
  });
266
269
  await expect(ports.sqlExecution?.execute({
267
270
  connectionId: 'warehouse',
@@ -277,7 +280,7 @@ describe('createLocalProjectMcpContextPorts', () => {
277
280
  url: 'env:DATABASE_URL',
278
281
  };
279
282
  await seedScanReport(project.projectDir);
280
- const ports = createLocalProjectMcpContextPorts(project);
283
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
281
284
  await expect(ports.entityDetails?.read({
282
285
  connectionId: 'warehouse',
283
286
  entities: [{ table: 'public.orders', columns: ['id'] }],
@@ -299,7 +302,7 @@ describe('createLocalProjectMcpContextPorts', () => {
299
302
  driver: 'postgres',
300
303
  url: 'env:DATABASE_URL',
301
304
  };
302
- const ports = createLocalProjectMcpContextPorts(project);
305
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
303
306
  await expect(ports.entityDetails?.read({
304
307
  connectionId: 'warehouse',
305
308
  entities: [{ table: 'public.orders' }],
@@ -337,7 +340,7 @@ describe('createLocalProjectMcpContextPorts', () => {
337
340
  },
338
341
  warnings: [],
339
342
  }, null, 2)}\n`, 'ktx', 'ktx@example.com', 'Seed dictionary profile');
340
- const ports = createLocalProjectMcpContextPorts(project);
343
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
341
344
  await expect(ports.dictionarySearch?.search({ values: ['paid'] })).resolves.toMatchObject({
342
345
  searched: [{ connectionId: 'warehouse', status: 'ready' }],
343
346
  results: [
@@ -355,7 +358,7 @@ describe('createLocalProjectMcpContextPorts', () => {
355
358
  driver: 'postgres',
356
359
  url: 'env:DATABASE_URL',
357
360
  };
358
- const ports = createLocalProjectMcpContextPorts(project);
361
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
359
362
  await expect(ports.dictionarySearch?.search({ values: ['paid'] })).resolves.toEqual({
360
363
  searched: [
361
364
  {
@@ -483,7 +486,7 @@ describe('createLocalProjectMcpContextPorts', () => {
483
486
  enrichmentState: { resumedStages: [], completedStages: [], failedStages: [] },
484
487
  createdAt: '2026-05-14T09:00:00.000Z',
485
488
  }, null, 2), 'ktx', 'ktx@example.com', 'seed scan report');
486
- const ports = createLocalProjectMcpContextPorts(project);
489
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
487
490
  const results = await ports.discover?.search({ query: 'paid orders', connectionId: 'warehouse', limit: 10 });
488
491
  expect(results).toEqual(expect.arrayContaining([
489
492
  expect.objectContaining({ kind: 'wiki', id: 'orders-playbook' }),
@@ -507,7 +510,7 @@ describe('createLocalProjectMcpContextPorts', () => {
507
510
  'Revenue is net of refunds.',
508
511
  '',
509
512
  ].join('\n'), 'ktx', 'ktx@example.com', 'Seed wiki');
510
- const ports = createLocalProjectMcpContextPorts(project);
513
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
511
514
  await expect(ports.knowledge?.read({ userId: 'local-user', key: 'revenue' })).resolves.toMatchObject({
512
515
  key: 'revenue',
513
516
  scope: 'GLOBAL',
@@ -549,7 +552,7 @@ describe('createLocalProjectMcpContextPorts', () => {
549
552
  '',
550
553
  ].join('\n'),
551
554
  });
552
- const ports = createLocalProjectMcpContextPorts(project);
555
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
553
556
  await expect(ports.semanticLayer?.readSource({ connectionId: 'warehouse', sourceName: 'orders' })).resolves.toMatchObject({
554
557
  sourceName: 'orders',
555
558
  yaml: expect.stringContaining('name: orders'),
@@ -557,7 +560,7 @@ describe('createLocalProjectMcpContextPorts', () => {
557
560
  });
558
561
  it('rejects path traversal keys before touching the project directory', async () => {
559
562
  const project = await initKtxProject({ projectDir: tempDir });
560
- const ports = createLocalProjectMcpContextPorts(project);
563
+ const ports = createLocalProjectMcpContextPorts(project, { embeddingService: null });
561
564
  await expect(ports.knowledge?.read({
562
565
  userId: 'local-user',
563
566
  key: '../outside',
@@ -603,7 +606,7 @@ describe('createLocalProjectMcpContextPorts', () => {
603
606
  })),
604
607
  generateSources: vi.fn(),
605
608
  };
606
- const ports = createLocalProjectMcpContextPorts(project, { semanticLayerCompute });
609
+ const ports = createLocalProjectMcpContextPorts(project, { semanticLayerCompute, embeddingService: null });
607
610
  await expect(ports.semanticLayer?.query({
608
611
  connectionId: 'warehouse',
609
612
  query: {
@@ -670,6 +673,7 @@ describe('createLocalProjectMcpContextPorts', () => {
670
673
  const ports = createLocalProjectMcpContextPorts(project, {
671
674
  semanticLayerCompute: compute,
672
675
  queryExecutor,
676
+ embeddingService: null,
673
677
  });
674
678
  const result = await ports.semanticLayer?.query({
675
679
  connectionId: 'warehouse',
@@ -123,7 +123,6 @@ describe('@ktx/context package exports', () => {
123
123
  expect(root.assertSearchBackendConformanceCase).toBeTypeOf('function');
124
124
  expect(root.assertSearchBackendCapabilities).toBeTypeOf('function');
125
125
  expect(root.createLocalKtxEmbeddingProviderFromConfig).toBeTypeOf('function');
126
- expect(root.MANAGED_SENTENCE_TRANSFORMERS_BASE_URL).toBe('managed:local-embeddings');
127
126
  expect(agent).toBeDefined();
128
127
  expect(agent.AgentRunnerService).toBeTypeOf('function');
129
128
  expect(root.AgentRunnerService).toBeTypeOf('function');
@@ -31,7 +31,7 @@ const vertexProviderSchema = z
31
31
  .describe('Google Vertex AI provider configuration.');
32
32
  const sentenceTransformersSchema = z
33
33
  .strictObject({
34
- base_url: z.string().default('').describe('Base URL of the sentence-transformers HTTP server. Empty string uses the managed local runtime.'),
34
+ base_url: z.string().default('').describe('Base URL of the sentence-transformers HTTP server. Leave empty (or omit) to use the project-managed local daemon.'),
35
35
  pathPrefix: z.string().optional().describe('Optional URL path prefix prepended to embedding requests.'),
36
36
  })
37
37
  .describe('Sentence-transformers embedding server configuration.');
@@ -736,16 +736,16 @@ describe('local scan enrichment', () => {
736
736
  executor.close();
737
737
  }
738
738
  });
739
- it('resolves gateway LLM providers and OpenAI embeddings from local scan config', () => {
739
+ it('resolves gateway LLM providers and passes injected embedding provider through to scan enrichment', () => {
740
740
  const createKtxLlmProvider = vi.fn(() => ({
741
741
  getModel: vi.fn().mockReturnValue({ modelId: 'provider/language-model', provider: 'gateway' }),
742
742
  }));
743
- const createKtxEmbeddingProvider = vi.fn(() => ({
743
+ const embeddingProvider = {
744
744
  dimensions: 1536,
745
745
  maxBatchSize: 8,
746
746
  embed: vi.fn(),
747
747
  [['embed', 'Many'].join('')]: vi.fn(),
748
- }));
748
+ };
749
749
  const providers = createLocalScanEnrichmentProvidersFromConfig({
750
750
  mode: 'llm',
751
751
  embeddings: {
@@ -763,12 +763,11 @@ describe('local scan enrichment', () => {
763
763
  models: { default: 'provider/language-model' },
764
764
  }, {
765
765
  createKtxLlmProvider: createKtxLlmProvider,
766
- createKtxEmbeddingProvider: createKtxEmbeddingProvider,
767
766
  env: { OPENAI_API_KEY: 'openai-key' }, // pragma: allowlist secret
767
+ embeddingProvider: embeddingProvider,
768
768
  });
769
769
  expect(providers?.embedding?.dimensions).toBe(1536);
770
770
  expect(providers?.embedding?.maxBatchSize).toBe(8);
771
771
  expect(createKtxLlmProvider).toHaveBeenCalledWith(expect.objectContaining({ backend: 'gateway', modelSlots: { default: 'provider/language-model' } }));
772
- expect(createKtxEmbeddingProvider).toHaveBeenCalledWith(expect.objectContaining({ backend: 'openai', model: 'provider/embedding-model' }));
773
772
  });
774
773
  });
@@ -1,4 +1,4 @@
1
- import type { createKtxEmbeddingProvider, createKtxLlmProvider } from '@ktx/llm';
1
+ import type { createKtxEmbeddingProvider, createKtxLlmProvider, KtxEmbeddingProvider } from '@ktx/llm';
2
2
  import { type LocalIngestRunRecord, type SourceAdapter } from '../ingest/index.js';
3
3
  import type { KtxProjectLlmConfig, KtxScanEnrichmentConfig } from '../project/config.js';
4
4
  import type { KtxLocalProject } from '../project/index.js';
@@ -21,6 +21,7 @@ export interface RunLocalScanOptions {
21
21
  enrichmentProviders?: KtxLocalScanEnrichmentProviders | null;
22
22
  enrichmentStateStore?: SqliteLocalScanEnrichmentStateStore | null;
23
23
  progress?: KtxProgressPort;
24
+ embeddingProvider?: KtxEmbeddingProvider | null;
24
25
  }
25
26
  export interface LocalScanRunResult {
26
27
  runId: string;
@@ -58,6 +59,7 @@ interface LocalScanEnrichmentProviderDeps {
58
59
  createKtxEmbeddingProvider?: typeof createKtxEmbeddingProvider;
59
60
  env?: NodeJS.ProcessEnv;
60
61
  projectDir?: string;
62
+ embeddingProvider?: KtxEmbeddingProvider | null;
61
63
  }
62
64
  export declare function createLocalScanEnrichmentProvidersFromConfig(config: KtxScanEnrichmentConfig, llmConfig: KtxProjectLlmConfig, deps?: LocalScanEnrichmentProviderDeps): KtxLocalScanEnrichmentProviders | null;
63
65
  export { filterSnapshotTables, resolveEnabledTables } from './enabled-tables.js';
@@ -1,5 +1,5 @@
1
1
  import { createDefaultLocalIngestAdapters, getLocalStageOnlyIngestStatus, runLocalStageOnlyIngest, } from '../ingest/index.js';
2
- import { createLocalKtxEmbeddingProviderFromConfig, createLocalKtxLlmRuntimeFromConfig, KtxScanEmbeddingPortAdapter, } from '../llm/index.js';
2
+ import { createLocalKtxLlmRuntimeFromConfig, KtxScanEmbeddingPortAdapter } from '../llm/index.js';
3
3
  import { ktxLocalStateDbPath } from '../project/local-state-db.js';
4
4
  import { redactKtxScanReport } from './credentials.js';
5
5
  import { filterSnapshotTables, resolveEnabledTables } from './enabled-tables.js';
@@ -65,7 +65,7 @@ export function createLocalScanEnrichmentProvidersFromConfig(config, llmConfig,
65
65
  ...deps,
66
66
  projectDir: deps.projectDir,
67
67
  });
68
- const embeddingProvider = createLocalKtxEmbeddingProviderFromConfig(config.embeddings, deps);
68
+ const embeddingProvider = deps.embeddingProvider ?? null;
69
69
  if (!llmRuntime || !embeddingProvider) {
70
70
  return null;
71
71
  }
@@ -227,6 +227,7 @@ export async function runLocalScan(options) {
227
227
  ? options.enrichmentProviders
228
228
  : createLocalScanEnrichmentProvidersFromConfig(options.project.config.scan.enrichment, options.project.config.llm, {
229
229
  projectDir: options.project.projectDir,
230
+ embeddingProvider: options.embeddingProvider ?? null,
230
231
  })
231
232
  : null;
232
233
  await options.progress?.update(0.15, 'Inspecting database schema');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaelio/ktx",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Standalone KTX context layer for database agents",
5
5
  "private": false,
6
6
  "type": "module",