ktx/packages/cli/src/setup-embeddings.ts

472 lines
16 KiB
TypeScript
Raw Permalink Normal View History

2026-05-10 23:12:26 +02:00
import { writeFile } from 'node:fs/promises';
2026-05-10 23:51:24 +02:00
import { resolveKtxConfigReference } from '@ktx/context/core';
2026-05-10 23:12:26 +02:00
import {
2026-05-10 23:51:24 +02:00
type KtxProjectConfig,
type KtxProjectEmbeddingConfig,
loadKtxProject,
markKtxSetupStateStepComplete,
readKtxSetupState,
2026-05-10 23:51:24 +02:00
serializeKtxProjectConfig,
} from '@ktx/context/project';
import { type KtxEmbeddingConfig, type KtxEmbeddingHealthCheckResult, runKtxEmbeddingHealthCheck } from '@ktx/llm';
import type { KtxCliIo } from './cli-runtime.js';
import { createStaticCliSpinner, type KtxCliSpinner } from './clack.js';
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
import {
ensureManagedLocalEmbeddingsDaemon,
managedLocalEmbeddingHealthConfig,
managedLocalEmbeddingProjectConfig,
type ManagedLocalEmbeddingsDaemon,
} from './managed-local-embeddings.js';
import type { KtxManagedPythonInstallPolicy } from './managed-python-command.js';
import { withTextInputNavigation } from './prompt-navigation.js';
2026-05-10 23:12:26 +02:00
import { envCredentialReference, writeProjectLocalSecretReference } from './setup-secrets.js';
import {
createKtxSetupPromptAdapter,
type KtxSetupPromptOption,
} from './setup-prompts.js';
2026-05-10 23:12:26 +02:00
2026-05-10 23:51:24 +02:00
export type KtxSetupEmbeddingBackend = 'openai' | 'sentence-transformers';
2026-05-10 23:12:26 +02:00
2026-05-10 23:51:24 +02:00
export interface KtxSetupEmbeddingsArgs {
2026-05-10 23:12:26 +02:00
projectDir: string;
inputMode: 'auto' | 'disabled';
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
cliVersion: string;
runtimeInstallPolicy: KtxManagedPythonInstallPolicy;
2026-05-10 23:51:24 +02:00
embeddingBackend?: KtxSetupEmbeddingBackend;
2026-05-10 23:12:26 +02:00
embeddingApiKeyEnv?: string;
embeddingApiKeyFile?: string;
forcePrompt?: boolean;
showPromptInstructions?: boolean;
skipEmbeddings: boolean;
}
2026-05-10 23:51:24 +02:00
export type KtxSetupEmbeddingsResult =
2026-05-10 23:12:26 +02:00
| { status: 'ready'; projectDir: string }
| { status: 'skipped'; projectDir: string }
| { status: 'back'; projectDir: string }
| { status: 'missing-input'; projectDir: string }
| { status: 'failed'; projectDir: string };
2026-05-10 23:51:24 +02:00
export interface KtxSetupEmbeddingsPromptAdapter {
select(options: { message: string; options: KtxSetupPromptOption[] }): Promise<string>;
2026-05-10 23:12:26 +02:00
password(options: { message: string }): Promise<string | undefined>;
cancel(message: string): void;
}
2026-05-10 23:51:24 +02:00
export interface KtxSetupEmbeddingsDeps {
2026-05-10 23:12:26 +02:00
env?: NodeJS.ProcessEnv;
2026-05-10 23:51:24 +02:00
prompts?: KtxSetupEmbeddingsPromptAdapter;
healthCheck?: (config: KtxEmbeddingConfig) => Promise<KtxEmbeddingHealthCheckResult>;
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
ensureLocalEmbeddings?: (options: {
cliVersion: string;
installPolicy: KtxManagedPythonInstallPolicy;
io: KtxCliIo;
}) => Promise<ManagedLocalEmbeddingsDaemon>;
spinner?: () => KtxCliSpinner;
2026-05-10 23:12:26 +02:00
}
2026-05-10 23:51:24 +02:00
type BackendChoice = KtxSetupEmbeddingBackend | 'back';
2026-05-10 23:12:26 +02:00
const DEFAULTS: Record<
2026-05-10 23:51:24 +02:00
KtxSetupEmbeddingBackend,
2026-05-10 23:12:26 +02:00
{ model: string; dimensions: number; envName?: string; baseUrl?: string; pathPrefix?: string }
> = {
openai: { model: 'text-embedding-3-small', dimensions: 1536, envName: 'OPENAI_API_KEY' },
'sentence-transformers': {
model: 'all-MiniLM-L6-v2',
dimensions: 384,
baseUrl: 'http://127.0.0.1:8765',
pathPrefix: '',
},
};
2026-05-10 23:51:24 +02:00
const LOCAL_EMBEDDING_BACKEND: KtxSetupEmbeddingBackend = 'sentence-transformers';
2026-05-10 23:12:26 +02:00
const EMBEDDING_OPTION_PROMPT_CONTEXT =
2026-05-10 23:51:24 +02:00
'KTX uses embeddings for semantic search over semantic-layer sources, wiki context, schema metadata, ' +
2026-05-10 23:12:26 +02:00
'and relationship evidence.';
const LOCAL_EMBEDDING_HEALTH_TIMEOUT_MS = 120_000;
2026-05-10 23:51:24 +02:00
function createPromptAdapter(): KtxSetupEmbeddingsPromptAdapter {
return createKtxSetupPromptAdapter({ selectCancelValue: 'back' });
2026-05-10 23:12:26 +02:00
}
async function hasCompletedEmbeddings(projectDir: string, config: KtxProjectConfig): Promise<boolean> {
2026-05-10 23:12:26 +02:00
return (
(await readKtxSetupState(projectDir)).completed_steps.includes('embeddings') &&
2026-05-10 23:12:26 +02:00
config.ingest.embeddings.backend !== 'none' &&
config.ingest.embeddings.backend !== 'deterministic' &&
typeof config.ingest.embeddings.model === 'string' &&
config.ingest.embeddings.model.length > 0 &&
config.ingest.embeddings.dimensions > 0
);
}
function buildProjectEmbeddingConfig(input: {
2026-05-10 23:51:24 +02:00
backend: KtxSetupEmbeddingBackend;
2026-05-10 23:12:26 +02:00
model: string;
dimensions: number;
credentialRef?: string;
2026-05-10 23:51:24 +02:00
}): KtxProjectEmbeddingConfig {
2026-05-10 23:12:26 +02:00
if (input.backend === 'openai') {
return {
backend: 'openai',
model: input.model,
dimensions: input.dimensions,
openai: {
...(input.credentialRef ? { api_key: input.credentialRef } : {}),
},
};
}
const defaults = DEFAULTS[input.backend];
return {
backend: input.backend,
model: input.model,
dimensions: input.dimensions,
sentenceTransformers: {
base_url: defaults.baseUrl ?? '',
pathPrefix: defaults.pathPrefix ?? '',
},
};
}
function buildHealthConfig(input: {
2026-05-10 23:51:24 +02:00
backend: KtxSetupEmbeddingBackend;
2026-05-10 23:12:26 +02:00
model: string;
dimensions: number;
credentialValue?: string;
2026-05-10 23:51:24 +02:00
}): KtxEmbeddingConfig {
2026-05-10 23:12:26 +02:00
if (input.backend === 'openai') {
return {
backend: 'openai',
model: input.model,
dimensions: input.dimensions,
openai: {
...(input.credentialValue ? { apiKey: input.credentialValue } : {}),
},
};
}
const defaults = DEFAULTS[input.backend];
return {
backend: input.backend,
model: input.model,
dimensions: input.dimensions,
sentenceTransformers: {
baseURL: defaults.baseUrl ?? '',
pathPrefix: defaults.pathPrefix ?? '',
},
};
}
2026-05-10 23:51:24 +02:00
function embeddingBackendDisplayName(backend: KtxSetupEmbeddingBackend): string {
2026-05-10 23:12:26 +02:00
if (backend === 'openai') {
return 'OpenAI';
}
return 'sentence-transformers';
}
2026-05-10 23:51:24 +02:00
async function persistEmbeddingConfig(projectDir: string, embeddings: KtxProjectEmbeddingConfig): Promise<void> {
const project = await loadKtxProject({ projectDir });
const config = {
...project.config,
ingest: {
...project.config.ingest,
embeddings,
},
scan: {
...project.config.scan,
enrichment: {
...project.config.scan.enrichment,
2026-05-10 23:12:26 +02:00
embeddings,
},
},
};
2026-05-10 23:51:24 +02:00
await writeFile(project.configPath, serializeKtxProjectConfig(config), 'utf-8');
await markKtxSetupStateStepComplete(projectDir, 'embeddings');
2026-05-10 23:12:26 +02:00
}
async function chooseCredentialRef(
2026-05-10 23:51:24 +02:00
backend: Extract<KtxSetupEmbeddingBackend, 'openai'>,
args: KtxSetupEmbeddingsArgs,
io: KtxCliIo,
deps: KtxSetupEmbeddingsDeps,
2026-05-10 23:12:26 +02:00
): Promise<{ status: 'ready'; ref: string; value: string } | { status: 'back' | 'missing-input' }> {
const env = deps.env ?? process.env;
if (args.embeddingApiKeyEnv) {
const ref = envCredentialReference(args.embeddingApiKeyEnv);
2026-05-10 23:51:24 +02:00
const value = resolveKtxConfigReference(ref, env);
2026-05-10 23:12:26 +02:00
if (!value) {
io.stderr.write(`Missing embedding API key: ${args.embeddingApiKeyEnv} is not set.\n`);
return { status: 'missing-input' };
}
return { status: 'ready', ref, value };
}
if (args.embeddingApiKeyFile) {
const ref = `file:${args.embeddingApiKeyFile}`;
let value: string | undefined;
try {
2026-05-10 23:51:24 +02:00
value = resolveKtxConfigReference(ref, env);
2026-05-10 23:12:26 +02:00
} catch {
value = undefined;
}
if (!value) {
io.stderr.write(`Missing embedding API key file: ${args.embeddingApiKeyFile}\n`);
return { status: 'missing-input' };
}
return { status: 'ready', ref, value };
}
if (args.inputMode === 'disabled') {
io.stderr.write('Missing embedding API key: pass --embedding-api-key-env or --embedding-api-key-file.\n');
return { status: 'missing-input' };
}
const defaultEnv = DEFAULTS[backend].envName ?? 'EMBEDDING_API_KEY';
const prompts = deps.prompts ?? createPromptAdapter();
const choice = await prompts.select({
2026-05-10 23:51:24 +02:00
message: `How should KTX find your ${embeddingBackendDisplayName(backend)} embedding API key?`,
2026-05-10 23:12:26 +02:00
options: [
{ value: 'env', label: `Use ${defaultEnv} from the environment` },
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
{ value: 'back', label: 'Back' },
],
});
if (choice === 'back') {
return { status: 'back' };
}
if (choice === 'paste') {
io.stdout.write(
`${[
2026-05-10 23:51:24 +02:00
`KTX will save the key in .ktx/secrets/${backend}-api-key with local file permissions,`,
'then write a file: reference in ktx.yaml.',
2026-05-10 23:12:26 +02:00
].join(' ')}\n`,
);
const value = await prompts.password({ message: withTextInputNavigation(`${backend} embedding API key`) });
if (value === undefined) {
return { status: 'back' };
}
if (!value.trim()) {
return { status: 'missing-input' };
}
const ref = await writeProjectLocalSecretReference({
projectDir: args.projectDir,
fileName: `${backend}-api-key`,
value,
});
return { status: 'ready', ref, value: value.trim() };
}
const ref = envCredentialReference(defaultEnv);
2026-05-10 23:51:24 +02:00
const value = resolveKtxConfigReference(ref, env);
2026-05-10 23:12:26 +02:00
if (!value) {
io.stderr.write(`Missing embedding API key: ${defaultEnv} is not set.\n`);
return { status: 'missing-input' };
}
return { status: 'ready', ref, value };
}
async function chooseEmbeddingBackend(
2026-05-10 23:51:24 +02:00
args: KtxSetupEmbeddingsArgs,
deps: KtxSetupEmbeddingsDeps,
2026-05-10 23:12:26 +02:00
): Promise<BackendChoice> {
if (args.embeddingBackend) {
return args.embeddingBackend;
}
if (args.inputMode === 'disabled') {
return LOCAL_EMBEDDING_BACKEND;
}
const choice = await (deps.prompts ?? createPromptAdapter()).select({
2026-05-10 23:51:24 +02:00
message: `Which embedding option should KTX use?\n\n${EMBEDDING_OPTION_PROMPT_CONTEXT}`,
2026-05-10 23:12:26 +02:00
options: [
{ value: 'sentence-transformers', label: 'Local sentence-transformers embeddings' },
{ value: 'openai', label: 'OpenAI embeddings', hint: 'recommended' },
2026-05-10 23:12:26 +02:00
{ value: 'back', label: 'Back' },
],
});
if (choice === 'openai' || choice === 'sentence-transformers' || choice === 'back') {
return choice;
}
return 'back';
}
function localEmbeddingSetupMessage(message: string): string {
return [
`Local embedding health check failed: ${message}`,
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
'Local embeddings use the KTX-managed Python runtime.',
2026-05-12 23:51:46 +02:00
'Prepare the runtime with: ktx dev runtime start --feature local-embeddings',
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
'Use --yes with setup to install and start the runtime without prompting.',
'The first run may download Python packages and the all-MiniLM-L6-v2 model.',
2026-05-10 23:12:26 +02:00
].join('\n');
}
async function promptAfterLocalEmbeddingFailure(
2026-05-10 23:51:24 +02:00
deps: KtxSetupEmbeddingsDeps,
): Promise<'retry' | Extract<KtxSetupEmbeddingBackend, 'openai'> | 'back'> {
2026-05-10 23:12:26 +02:00
const choice = await (deps.prompts ?? createPromptAdapter()).select({
2026-05-10 23:51:24 +02:00
message: 'Local embeddings are not reachable. Start the local KTX daemon, then retry.',
2026-05-10 23:12:26 +02:00
options: [
{ value: 'retry', label: 'Retry' },
{ value: 'openai', label: 'Use OpenAI embeddings' },
{ value: 'back', label: 'Back' },
],
});
if (choice === 'openai' || choice === 'back') {
return choice;
}
return 'retry';
}
2026-05-10 23:51:24 +02:00
function healthCheckStartText(backend: KtxSetupEmbeddingBackend, model: string, dimensions: number): string {
2026-05-10 23:12:26 +02:00
if (backend === LOCAL_EMBEDDING_BACKEND) {
return `Testing local embeddings (${model})`;
2026-05-10 23:12:26 +02:00
}
return `Checking ${backend} embeddings (${model}, ${dimensions} dimensions).`;
}
function startHealthCheckProgress(
spinner: KtxCliSpinner,
message: string,
): { succeed(msg: string): void; fail(msg: string): void } {
spinner.start(message);
2026-05-10 23:12:26 +02:00
return {
succeed(msg: string) {
spinner.stop(msg);
2026-05-10 23:12:26 +02:00
},
fail(msg: string) {
spinner.error(msg);
2026-05-10 23:12:26 +02:00
},
};
}
2026-05-10 23:51:24 +02:00
export async function runKtxSetupEmbeddingsStep(
args: KtxSetupEmbeddingsArgs,
io: KtxCliIo,
deps: KtxSetupEmbeddingsDeps = {},
): Promise<KtxSetupEmbeddingsResult> {
2026-05-10 23:12:26 +02:00
if (args.skipEmbeddings) {
io.stdout.write('│ Embeddings setup skipped.\n');
2026-05-10 23:12:26 +02:00
return { status: 'skipped', projectDir: args.projectDir };
}
2026-05-10 23:51:24 +02:00
const project = await loadKtxProject({ projectDir: args.projectDir });
2026-05-10 23:12:26 +02:00
if (
args.forcePrompt !== true &&
(await hasCompletedEmbeddings(args.projectDir, project.config)) &&
2026-05-10 23:12:26 +02:00
!args.embeddingBackend &&
!args.embeddingApiKeyEnv &&
!args.embeddingApiKeyFile
) {
io.stdout.write(`│ Embeddings ready: yes (${project.config.ingest.embeddings.model})\n`);
2026-05-10 23:12:26 +02:00
return { status: 'ready', projectDir: args.projectDir };
}
const healthCheck =
deps.healthCheck ??
2026-05-10 23:51:24 +02:00
((config: KtxEmbeddingConfig) =>
runKtxEmbeddingHealthCheck(config, { timeoutMs: LOCAL_EMBEDDING_HEALTH_TIMEOUT_MS }));
let selectedBackend: KtxSetupEmbeddingBackend | undefined;
2026-05-10 23:12:26 +02:00
while (true) {
if (!selectedBackend) {
const backend = await chooseEmbeddingBackend(args, deps);
if (backend === 'back') {
return { status: 'back', projectDir: args.projectDir };
}
selectedBackend = backend;
}
const defaults = DEFAULTS[selectedBackend];
const model = defaults.model;
const dimensions = defaults.dimensions;
let credentialRef: string | undefined;
let credentialValue: string | undefined;
if (selectedBackend === 'openai') {
const credential = await chooseCredentialRef(selectedBackend, args, io, deps);
if (credential.status === 'back' && !args.embeddingBackend && args.inputMode !== 'disabled') {
selectedBackend = undefined;
continue;
}
if (credential.status !== 'ready') {
return { status: credential.status, projectDir: args.projectDir };
}
credentialRef = credential.ref;
credentialValue = credential.value;
}
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
let managedLocalEmbeddings: ManagedLocalEmbeddingsDaemon | undefined;
if (selectedBackend === LOCAL_EMBEDDING_BACKEND) {
const ensureLocalEmbeddings = deps.ensureLocalEmbeddings ?? ensureManagedLocalEmbeddingsDaemon;
try {
managedLocalEmbeddings = await ensureLocalEmbeddings({
cliVersion: args.cliVersion,
projectDir: args.projectDir,
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
installPolicy: args.runtimeInstallPolicy,
io,
});
} catch (error) {
io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
return { status: 'failed', projectDir: args.projectDir };
}
}
const healthConfig =
selectedBackend === LOCAL_EMBEDDING_BACKEND && managedLocalEmbeddings
? managedLocalEmbeddingHealthConfig({
baseUrl: managedLocalEmbeddings.baseUrl,
model,
dimensions,
})
: buildHealthConfig({
backend: selectedBackend,
model,
dimensions,
credentialValue,
});
const healthSpinner = (deps.spinner ?? (() => createStaticCliSpinner(io)))();
const progress = startHealthCheckProgress(healthSpinner, healthCheckStartText(selectedBackend, model, dimensions));
2026-05-10 23:51:24 +02:00
let health: KtxEmbeddingHealthCheckResult;
2026-05-10 23:12:26 +02:00
try {
health = await healthCheck(healthConfig);
} catch (error) {
progress.fail('Embedding test failed');
throw error;
}
if (health.ok) {
progress.succeed(`Embedding test passed (${model}, ${dimensions} dimensions)`);
await persistEmbeddingConfig(
args.projectDir,
feat: npm-managed Python runtime for @kaelio/ktx (#7) * docs: add npm managed python runtime design * build: add bundled python runtime wheel builder * build: make local embedding dependencies optional * build: bundle python runtime wheel in cli artifacts * build: track bundled python runtime release artifact * test: verify bundled python runtime wheel * docs: add plan for bundled python runtime wheel * test: cover managed python runtime lifecycle * feat: add managed python runtime installer * feat: add runtime command runner * feat: expose runtime management commands * test: verify managed python runtime commands * docs: add plan for managed python runtime installer * feat: add managed python command helper * feat: use managed runtime for sl query compute * feat: route sl query managed runtime policy * docs: add plan for managed runtime sl query integration * feat: add managed runtime daemon metadata * feat: manage python daemon lifecycle * feat: add runtime daemon start stop commands * fix: verify managed runtime daemon lifecycle * docs: add plan for managed runtime daemon lifecycle * feat: add managed local embeddings config marker * feat: add managed local embeddings daemon helper * feat: use managed runtime for local embedding setup * feat: pass managed runtime policy through setup * docs: add plan for managed local embeddings runtime * feat: read CLI package metadata dynamically * feat: assemble public kaelio ktx npm package * feat: release one public kaelio ktx npm artifact * test: cover public kaelio ktx package invocations * chore: verify public kaelio ktx package artifacts * docs: add plan for public kaelio ktx npm package * test: verify managed runtime in public package smoke * test: finalize managed runtime release smoke * docs: add plan for managed runtime release smoke * test: specify local embeddings release smoke * feat: add local embeddings runtime smoke * chore: register local embeddings smoke * fix: verify local embeddings smoke * fix: restore artifact smoke python env helper * docs: add plan for managed local embeddings release smoke * refactor: share managed runtime install policy parsing * feat: use managed runtime for agent semantic queries * feat: use managed runtime for MCP semantic compute * docs: add plan for managed agent and MCP semantic runtime * feat(cli): add managed daemon HTTP helpers * feat(cli): route local adapters through managed daemon * feat(cli): use managed daemon for ingest helpers * feat(cli): pass managed daemon options to scan * feat(context): pass MCP ingest pull config options * feat(cli): pass managed daemon options to serve ingest * test: verify managed local ingest daemon runtime * docs: add plan for managed local ingest daemon runtime * docs: align managed runtime examples * docs: add plan for managed runtime docs cleanup * test: cover published package runtime smoke commands * test: validate published package smoke outputs * docs: add plan for published package runtime smoke * build: stamp public npm package version * release: add npm public release policy * release: add guarded npm publish script * release: document public npm release handoff * docs: add plan for public npm release handoff * test: cover managed runtime prune in package smoke * docs: document managed runtime prune * docs: add plan for managed runtime prune smoke and docs * chore: encode uv runtime prerequisite policy * fix: clarify missing uv runtime error * docs: document uv runtime prerequisite * docs: add plan for uv runtime prerequisite contract * refactor: limit release artifacts to public package runtime * chore: align release policy with bundled runtime wheel * docs: describe single public runtime artifact surface * test: verify single public runtime artifact contract * docs: add plan for single public runtime artifact cleanup * fix: align local embeddings smoke with public version * docs: add plan for local embeddings smoke public version * release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag Publish target moves to the pre-release version 0.1.0-rc.0 under the next dist-tag so npm install @kaelio/ktx (which resolves to latest) does not pick up the soft-launch build. Users opt in via @kaelio/ktx@next. * Fix release script boundary checks * Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00
selectedBackend === LOCAL_EMBEDDING_BACKEND
? managedLocalEmbeddingProjectConfig({ model, dimensions })
: buildProjectEmbeddingConfig({
backend: selectedBackend,
model,
dimensions,
credentialRef,
}),
2026-05-10 23:12:26 +02:00
);
io.stdout.write(`│ Embeddings ready: yes (${model}, ${dimensions} dimensions)\n`);
2026-05-10 23:12:26 +02:00
return { status: 'ready', projectDir: args.projectDir };
}
progress.fail('Embedding test failed');
io.stderr.write(
selectedBackend === 'sentence-transformers'
? `${localEmbeddingSetupMessage(health.message)}\n`
: `Embedding health check failed: ${health.message}\n`,
);
if (args.inputMode === 'disabled') {
return { status: 'failed', projectDir: args.projectDir };
}
if (selectedBackend !== 'sentence-transformers' && (args.embeddingApiKeyEnv || args.embeddingApiKeyFile)) {
return { status: 'failed', projectDir: args.projectDir };
}
const nextAction =
selectedBackend === 'sentence-transformers' ? await promptAfterLocalEmbeddingFailure(deps) : 'retry';
if (nextAction === 'back') {
return { status: 'back', projectDir: args.projectDir };
}
if (nextAction === 'openai') {
selectedBackend = nextAction;
}
}
}