mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-28 08:49:38 +02:00
fix(cli): resolve embedding provider explicitly and surface lane status in sl search (#192)
* feat(cli): add tryUseManagedLocalEmbeddingsDaemon for read-only callers * feat(cli): add resolveProjectEmbeddingProvider helper * fix(cli): wire sl search through resolveProjectEmbeddingProvider so semantic lane works * fix(cli): wire wiki/knowledge search through resolveProjectEmbeddingProvider * feat(cli): surface embeddings-unavailable status when sl search returns empty * refactor(cli): route admin reindex through resolveProjectEmbeddingProvider * refactor: pass embeddingProvider into ingest/scan instead of resolving inside @ktx/context * refactor(mcp): resolve embedding provider in CLI factory, pass into context ports * refactor(context): delete MANAGED_SENTENCE_TRANSFORMERS_BASE_URL sentinel * refactor(cli): delete sentinel-based managed-embeddings indirection * chore: scrub stale managed-embeddings sentinel references from tests and smoke script * chore: unexport unused EmbeddingResolutionMode alias * fix(cli): force pathPrefix="" when targeting the managed embeddings daemon The managed daemon serves /embeddings/compute directly. The default pathPrefix in @ktx/llm is /api, so omitting sentenceTransformers from ktx.yaml produced /api/embeddings/compute -> 404. The resolver now sets pathPrefix='' explicitly when wiring the managed daemon URL, matching what the daemon actually exposes.
This commit is contained in:
parent
56a967278a
commit
9d92c79988
36 changed files with 750 additions and 442 deletions
|
|
@ -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,12 +197,24 @@ 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,
|
||||
});
|
||||
if (sources.length === 0 && resolution.kind === 'managed-unavailable' && !args.json) {
|
||||
const { SYMBOLS } = await import('./io/symbols.js');
|
||||
io.stderr.write(`embeddings: unavailable ${SYMBOLS.emDash} ${resolution.reason}\n`);
|
||||
}
|
||||
await printSlSources({
|
||||
rows: sources,
|
||||
emptyMessage: `No semantic-layer sources matched "${args.query}" in ${project.projectDir}`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue