mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-16 08:25:14 +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,91 +1,20 @@
|
|||
import { MANAGED_SENTENCE_TRANSFORMERS_BASE_URL } from '@ktx/context';
|
||||
import { loadKtxProject, type KtxLocalProject } from '@ktx/context/project';
|
||||
import type { KtxProjectConfig, KtxProjectEmbeddingConfig } from '@ktx/context/project';
|
||||
import type { KtxCliIo } from './cli-runtime.js';
|
||||
import {
|
||||
ensureManagedLocalEmbeddingsDaemon,
|
||||
type ManagedLocalEmbeddingsDaemon,
|
||||
} from './managed-local-embeddings.js';
|
||||
import type { KtxManagedPythonInstallPolicy } from './managed-python-command.js';
|
||||
|
||||
export interface LoadKtxCliProjectOptions {
|
||||
projectDir: string;
|
||||
cliVersion: string;
|
||||
installPolicy: KtxManagedPythonInstallPolicy;
|
||||
io: KtxCliIo;
|
||||
}
|
||||
|
||||
export interface LoadKtxCliProjectDeps {
|
||||
loadProject?: typeof loadKtxProject;
|
||||
ensureLocalEmbeddings?: (
|
||||
options: Parameters<typeof ensureManagedLocalEmbeddingsDaemon>[0],
|
||||
) => Promise<ManagedLocalEmbeddingsDaemon>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thin wrapper around `loadKtxProject`. Kept as a single entrypoint so the CLI can grow shared
|
||||
* pre-load behavior later (telemetry, project lock, etc.). Today it does no extra work.
|
||||
*/
|
||||
export async function loadKtxCliProject(
|
||||
options: LoadKtxCliProjectOptions,
|
||||
deps: LoadKtxCliProjectDeps = {},
|
||||
): Promise<KtxLocalProject> {
|
||||
const loadProject = deps.loadProject ?? loadKtxProject;
|
||||
const ensureLocalEmbeddings = deps.ensureLocalEmbeddings ?? ensureManagedLocalEmbeddingsDaemon;
|
||||
|
||||
const project = await loadProject({ projectDir: options.projectDir });
|
||||
if (!projectNeedsManagedLocalEmbeddings(project.config)) {
|
||||
return project;
|
||||
}
|
||||
|
||||
const daemon = await ensureLocalEmbeddings({
|
||||
cliVersion: options.cliVersion,
|
||||
projectDir: options.projectDir,
|
||||
installPolicy: options.installPolicy,
|
||||
io: options.io,
|
||||
});
|
||||
|
||||
return {
|
||||
...project,
|
||||
config: substituteManagedLocalEmbeddingsUrl(project.config, daemon.baseUrl),
|
||||
};
|
||||
}
|
||||
|
||||
export function projectNeedsManagedLocalEmbeddings(config: KtxProjectConfig): boolean {
|
||||
return (
|
||||
embeddingUsesManagedSentinel(config.ingest.embeddings) ||
|
||||
embeddingUsesManagedSentinel(config.scan.enrichment.embeddings)
|
||||
);
|
||||
}
|
||||
|
||||
export function substituteManagedLocalEmbeddingsUrl(
|
||||
config: KtxProjectConfig,
|
||||
baseUrl: string,
|
||||
): KtxProjectConfig {
|
||||
const ingestEmbeddings = rewriteManagedEmbeddingConfig(config.ingest.embeddings, baseUrl);
|
||||
const scanEnrichmentEmbeddings = rewriteManagedEmbeddingConfig(config.scan.enrichment.embeddings, baseUrl);
|
||||
return {
|
||||
...config,
|
||||
ingest: { ...config.ingest, embeddings: ingestEmbeddings },
|
||||
scan: {
|
||||
...config.scan,
|
||||
enrichment: { ...config.scan.enrichment, embeddings: scanEnrichmentEmbeddings },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function embeddingUsesManagedSentinel(embedding: KtxProjectEmbeddingConfig | undefined): boolean {
|
||||
return embedding?.sentenceTransformers?.base_url === MANAGED_SENTENCE_TRANSFORMERS_BASE_URL;
|
||||
}
|
||||
|
||||
function rewriteManagedEmbeddingConfig<T extends KtxProjectEmbeddingConfig | undefined>(
|
||||
embedding: T,
|
||||
baseUrl: string,
|
||||
): T {
|
||||
if (!embedding || !embeddingUsesManagedSentinel(embedding)) {
|
||||
return embedding;
|
||||
}
|
||||
return {
|
||||
...embedding,
|
||||
sentenceTransformers: {
|
||||
...embedding.sentenceTransformers,
|
||||
base_url: baseUrl,
|
||||
},
|
||||
} as T;
|
||||
return (deps.loadProject ?? loadKtxProject)({ projectDir: options.projectDir });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue