mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-10 08:05:14 +02:00
fix(cli): wire sl search through resolveProjectEmbeddingProvider so semantic lane works
This commit is contained in:
parent
039e686668
commit
8aa9ab8843
4 changed files with 105 additions and 28 deletions
|
|
@ -77,6 +77,7 @@ export function registerSlCommands(program: Command, context: KtxCliCommandConte
|
|||
connectionId: options.connectionId,
|
||||
output: options.output,
|
||||
json: options.json,
|
||||
cliVersion: context.packageInfo.version,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -88,6 +89,7 @@ export function registerSlCommands(program: Command, context: KtxCliCommandConte
|
|||
...(options.limit !== undefined ? { limit: options.limit } : {}),
|
||||
output: options.output,
|
||||
json: options.json,
|
||||
cliVersion: context.packageInfo.version,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ describe('runKtxCli', () => {
|
|||
),
|
||||
).resolves.toBe(0);
|
||||
expect(sl).toHaveBeenCalledWith(
|
||||
{
|
||||
expect.objectContaining({
|
||||
command: 'search',
|
||||
projectDir: tempDir,
|
||||
connectionId: 'warehouse',
|
||||
|
|
@ -252,7 +252,7 @@ describe('runKtxCli', () => {
|
|||
limit: 5,
|
||||
json: true,
|
||||
output: undefined,
|
||||
},
|
||||
}),
|
||||
searchIo.io,
|
||||
);
|
||||
|
||||
|
|
@ -261,13 +261,13 @@ describe('runKtxCli', () => {
|
|||
runKtxCli(['--project-dir', tempDir, 'sl', '--connection-id', 'warehouse', '--json'], bareIo.io, { sl }),
|
||||
).resolves.toBe(0);
|
||||
expect(sl).toHaveBeenLastCalledWith(
|
||||
{
|
||||
expect.objectContaining({
|
||||
command: 'list',
|
||||
projectDir: tempDir,
|
||||
connectionId: 'warehouse',
|
||||
json: true,
|
||||
output: undefined,
|
||||
},
|
||||
}),
|
||||
bareIo.io,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,12 +77,24 @@ describe('runKtxSl', () => {
|
|||
expect(validateIo.stdout()).toContain('Valid semantic-layer source: warehouse/orders');
|
||||
|
||||
const listIo = makeIo();
|
||||
await expect(runKtxSl({ command: 'list', projectDir, connectionId: 'warehouse' }, listIo.io)).resolves.toBe(0);
|
||||
await expect(
|
||||
runKtxSl({ command: 'list', projectDir, connectionId: 'warehouse', cliVersion: '0.0.0-test' }, listIo.io),
|
||||
).resolves.toBe(0);
|
||||
expect(listIo.stdout()).toContain('warehouse\torders\tcolumns=1\tmeasures=0\tjoins=0');
|
||||
|
||||
const searchIo = makeIo();
|
||||
await expect(
|
||||
runKtxSl({ command: 'search', projectDir, connectionId: 'warehouse', query: 'order', json: true }, searchIo.io),
|
||||
runKtxSl(
|
||||
{
|
||||
command: 'search',
|
||||
projectDir,
|
||||
connectionId: 'warehouse',
|
||||
query: 'order',
|
||||
json: true,
|
||||
cliVersion: '0.0.0-test',
|
||||
},
|
||||
searchIo.io,
|
||||
),
|
||||
).resolves.toBe(0);
|
||||
expect(JSON.parse(searchIo.stdout())).toMatchObject({
|
||||
kind: 'list',
|
||||
|
|
@ -106,7 +118,14 @@ describe('runKtxSl', () => {
|
|||
const searchIo = makeIo();
|
||||
await expect(
|
||||
runKtxSl(
|
||||
{ command: 'search', projectDir, connectionId: 'warehouse', query: 'order', output: 'pretty' },
|
||||
{
|
||||
command: 'search',
|
||||
projectDir,
|
||||
connectionId: 'warehouse',
|
||||
query: 'order',
|
||||
output: 'pretty',
|
||||
cliVersion: '0.0.0-test',
|
||||
},
|
||||
searchIo.io,
|
||||
),
|
||||
).resolves.toBe(0);
|
||||
|
|
@ -136,7 +155,14 @@ describe('runKtxSl', () => {
|
|||
const listIo = makeIo();
|
||||
await expect(
|
||||
runKtxSl(
|
||||
{ command: 'search', projectDir, connectionId: 'warehouse', query: 'paid', json: true },
|
||||
{
|
||||
command: 'search',
|
||||
projectDir,
|
||||
connectionId: 'warehouse',
|
||||
query: 'paid',
|
||||
json: true,
|
||||
cliVersion: '0.0.0-test',
|
||||
},
|
||||
listIo.io,
|
||||
),
|
||||
).resolves.toBe(0);
|
||||
|
|
@ -575,7 +601,7 @@ joins: []
|
|||
|
||||
const listIo = makeIo();
|
||||
const code = await runKtxSl(
|
||||
{ command: 'list', projectDir, connectionId: 'warehouse', output: 'json' },
|
||||
{ command: 'list', projectDir, connectionId: 'warehouse', output: 'json', cliVersion: '0.0.0-test' },
|
||||
listIo.io,
|
||||
);
|
||||
expect(code).toBe(0);
|
||||
|
|
@ -601,13 +627,45 @@ joins: []
|
|||
});
|
||||
});
|
||||
|
||||
it('passes a managed-daemon-backed embedding service into the search', async () => {
|
||||
const projectDir = join(tempDir, 'resolver-project');
|
||||
const project = await initKtxProject({ projectDir });
|
||||
const search = vi.fn(async () => []);
|
||||
const searchIo = makeIo();
|
||||
await expect(
|
||||
runKtxSl(
|
||||
{
|
||||
command: 'search',
|
||||
projectDir: project.projectDir,
|
||||
query: 'income',
|
||||
cliVersion: '0.5.0',
|
||||
json: true,
|
||||
},
|
||||
searchIo.io,
|
||||
{
|
||||
loadProject: async () => project,
|
||||
resolveEmbeddingProvider: async () => ({
|
||||
kind: 'managed-running',
|
||||
provider: { id: 'fake' } as never,
|
||||
baseUrl: 'http://127.0.0.1:51234',
|
||||
}),
|
||||
searchLocalSlSources: search,
|
||||
},
|
||||
),
|
||||
).resolves.toBe(0);
|
||||
expect(search).toHaveBeenCalledWith(
|
||||
project,
|
||||
expect.objectContaining({ embeddingService: expect.any(Object) }),
|
||||
);
|
||||
});
|
||||
|
||||
it('emits sl list with grouping and Clack-style framing when output=pretty', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await seedSlSource({ projectDir });
|
||||
|
||||
const listIo = makeIo();
|
||||
const code = await runKtxSl(
|
||||
{ command: 'list', projectDir, connectionId: 'warehouse', output: 'pretty' },
|
||||
{ command: 'list', projectDir, connectionId: 'warehouse', output: 'pretty', cliVersion: '0.0.0-test' },
|
||||
listIo.io,
|
||||
);
|
||||
expect(code).toBe(0);
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { createDefaultLocalQueryExecutor, type KtxSqlQueryExecutorPort } from '@ktx/context/connections';
|
||||
import {
|
||||
createLocalKtxEmbeddingProviderFromConfig,
|
||||
KtxIngestEmbeddingPortAdapter,
|
||||
type KtxEmbeddingPort,
|
||||
} from '@ktx/context';
|
||||
import { KtxIngestEmbeddingPortAdapter, type KtxEmbeddingPort } from '@ktx/context';
|
||||
import type { KtxSemanticLayerComputePort } from '@ktx/context/daemon';
|
||||
import { loadKtxProject, type KtxLocalProject } from '@ktx/context/project';
|
||||
import {
|
||||
compileLocalSlQuery,
|
||||
listLocalSlSources,
|
||||
readLocalSlSource,
|
||||
searchLocalSlSources,
|
||||
searchLocalSlSources as defaultSearchLocalSlSources,
|
||||
validateLocalSlSource,
|
||||
type LocalSlSourceSearchResult,
|
||||
type LocalSlSourceSummary,
|
||||
type SemanticLayerQueryInput,
|
||||
} from '@ktx/context/sl';
|
||||
import {
|
||||
resolveProjectEmbeddingProvider,
|
||||
type EmbeddingProviderResolution,
|
||||
} from './embedding-resolution.js';
|
||||
import type { PrintListColumn } from './io/print-list.js';
|
||||
import {
|
||||
createManagedPythonSemanticLayerComputePort,
|
||||
|
|
@ -29,7 +29,14 @@ profileMark('module:sl');
|
|||
type SlQueryFormat = 'json' | 'sql';
|
||||
|
||||
export type KtxSlArgs =
|
||||
| { command: 'list'; projectDir: string; connectionId?: string; output?: string; json?: boolean }
|
||||
| {
|
||||
command: 'list';
|
||||
projectDir: string;
|
||||
connectionId?: string;
|
||||
output?: string;
|
||||
json?: boolean;
|
||||
cliVersion: string;
|
||||
}
|
||||
| {
|
||||
command: 'search';
|
||||
projectDir: string;
|
||||
|
|
@ -38,6 +45,7 @@ export type KtxSlArgs =
|
|||
limit?: number;
|
||||
output?: string;
|
||||
json?: boolean;
|
||||
cliVersion: string;
|
||||
}
|
||||
| { command: 'validate'; projectDir: string; connectionId: string; sourceName: string }
|
||||
| {
|
||||
|
|
@ -60,8 +68,8 @@ interface KtxSlIo {
|
|||
|
||||
interface KtxSlDeps {
|
||||
loadProject?: typeof loadKtxProject;
|
||||
embeddingService?: KtxEmbeddingPort | null;
|
||||
createEmbeddingProvider?: typeof createLocalKtxEmbeddingProviderFromConfig;
|
||||
resolveEmbeddingProvider?: typeof resolveProjectEmbeddingProvider;
|
||||
searchLocalSlSources?: typeof defaultSearchLocalSlSources;
|
||||
createSemanticLayerCompute?: () => KtxSemanticLayerComputePort;
|
||||
createManagedSemanticLayerCompute?: (options: {
|
||||
cliVersion: string;
|
||||
|
|
@ -71,14 +79,15 @@ interface KtxSlDeps {
|
|||
createQueryExecutor?: () => KtxSqlQueryExecutorPort;
|
||||
}
|
||||
|
||||
function slSearchEmbeddingService(project: KtxLocalProject, deps: KtxSlDeps): KtxEmbeddingPort | null {
|
||||
if ('embeddingService' in deps) {
|
||||
return deps.embeddingService ?? null;
|
||||
function resolutionToEmbeddingPort(resolution: EmbeddingProviderResolution): KtxEmbeddingPort | null {
|
||||
if (
|
||||
resolution.kind === 'configured' ||
|
||||
resolution.kind === 'managed-running' ||
|
||||
resolution.kind === 'managed-started'
|
||||
) {
|
||||
return new KtxIngestEmbeddingPortAdapter(resolution.provider);
|
||||
}
|
||||
const provider = (deps.createEmbeddingProvider ?? createLocalKtxEmbeddingProviderFromConfig)(
|
||||
project.config.ingest.embeddings,
|
||||
);
|
||||
return provider ? new KtxIngestEmbeddingPortAdapter(provider) : null;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function printSlSources(input: {
|
||||
|
|
@ -188,10 +197,18 @@ export async function runKtxSl(args: KtxSlArgs, io: KtxSlIo = process, deps: Ktx
|
|||
return 0;
|
||||
}
|
||||
if (args.command === 'search') {
|
||||
const sources = await searchLocalSlSources(project, {
|
||||
const resolver = deps.resolveEmbeddingProvider ?? resolveProjectEmbeddingProvider;
|
||||
const resolution = await resolver(project, {
|
||||
mode: 'use-if-running',
|
||||
cliVersion: args.cliVersion,
|
||||
io,
|
||||
});
|
||||
const embeddingService = resolutionToEmbeddingPort(resolution);
|
||||
const search = deps.searchLocalSlSources ?? defaultSearchLocalSlSources;
|
||||
const sources = await search(project, {
|
||||
connectionId: args.connectionId,
|
||||
query: args.query,
|
||||
embeddingService: slSearchEmbeddingService(project, deps),
|
||||
embeddingService,
|
||||
limit: args.limit,
|
||||
});
|
||||
await printSlSources({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue