ktx/packages/llm/src/embedding-health.ts

55 lines
2 KiB
TypeScript
Raw Permalink Normal View History

2026-05-10 23:51:24 +02:00
import { createKtxEmbeddingProvider, type KtxEmbeddingProviderDeps } from './embedding-provider.js';
import type { KtxEmbeddingConfig } from './types.js';
2026-05-10 23:12:26 +02:00
2026-05-10 23:51:24 +02:00
export type KtxEmbeddingHealthCheckResult = { ok: true } | { ok: false; message: string };
2026-05-10 23:12:26 +02:00
2026-05-10 23:51:24 +02:00
export interface KtxEmbeddingHealthCheckOptions {
2026-05-10 23:12:26 +02:00
text?: string;
timeoutMs?: number;
2026-05-10 23:51:24 +02:00
deps?: KtxEmbeddingProviderDeps;
2026-05-10 23:12:26 +02:00
}
2026-05-10 23:51:24 +02:00
function redactHealthCheckMessage(message: string, config: KtxEmbeddingConfig): string {
2026-05-10 23:12:26 +02:00
const secrets = [config.openai?.apiKey].filter(
(value): value is string => typeof value === 'string' && value.length > 0,
);
return secrets.reduce((current, secret) => current.split(secret).join('[redacted]'), message);
}
async function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
let timeout: NodeJS.Timeout | undefined;
const timeoutPromise = new Promise<never>((_resolve, reject) => {
timeout = setTimeout(() => reject(new Error(`Embedding health check timed out after ${timeoutMs}ms`)), timeoutMs);
});
try {
return await Promise.race([promise, timeoutPromise]);
} finally {
if (timeout) {
clearTimeout(timeout);
}
}
}
2026-05-10 23:51:24 +02:00
export async function runKtxEmbeddingHealthCheck(
config: KtxEmbeddingConfig,
options: KtxEmbeddingHealthCheckOptions = {},
): Promise<KtxEmbeddingHealthCheckResult> {
2026-05-10 23:12:26 +02:00
try {
2026-05-10 23:51:24 +02:00
const provider = createKtxEmbeddingProvider(config, options.deps);
2026-05-10 23:12:26 +02:00
const embedding = await withTimeout(
2026-05-10 23:51:24 +02:00
provider.embed(options.text ?? 'KTX embedding health check'),
2026-05-10 23:12:26 +02:00
options.timeoutMs ?? 15_000,
);
if (embedding.length !== config.dimensions) {
return {
ok: false,
message: `Embedding provider ${config.backend} returned vector with ${embedding.length} dimensions; expected ${config.dimensions}`,
};
}
return { ok: true };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return { ok: false, message: redactHealthCheckMessage(message, config) };
}
}