rename klo to ktx

This commit is contained in:
Andrey Avtomonov 2026-05-10 23:51:24 +02:00
parent 1a42152e6f
commit 3ce510b55b
704 changed files with 10205 additions and 10255 deletions

View file

@ -3,13 +3,13 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import {
createJsonlKloLlmDebugRequestRecorder,
summarizeKloLlmDebugRequest,
createJsonlKtxLlmDebugRequestRecorder,
summarizeKtxLlmDebugRequest,
} from './debug-request-recorder.js';
describe('summarizeKloLlmDebugRequest', () => {
describe('summarizeKtxLlmDebugRequest', () => {
it('records providerOptions positions without message text or tool schemas', () => {
const summary = summarizeKloLlmDebugRequest({
const summary = summarizeKtxLlmDebugRequest({
operationName: 'ingest-bundle-wu',
source: 'metabase',
jobId: 'job-1',
@ -81,7 +81,7 @@ describe('summarizeKloLlmDebugRequest', () => {
});
});
describe('createJsonlKloLlmDebugRequestRecorder', () => {
describe('createJsonlKtxLlmDebugRequestRecorder', () => {
let tempDir: string | undefined;
afterEach(async () => {
@ -92,9 +92,9 @@ describe('createJsonlKloLlmDebugRequestRecorder', () => {
});
it('appends one JSON object per recorded request', async () => {
tempDir = await mkdtemp(join(tmpdir(), 'klo-llm-debug-'));
tempDir = await mkdtemp(join(tmpdir(), 'ktx-llm-debug-'));
const filePath = join(tempDir, 'nested', 'llm-debug.jsonl');
const recorder = createJsonlKloLlmDebugRequestRecorder(filePath);
const recorder = createJsonlKtxLlmDebugRequestRecorder(filePath);
await recorder.record({
timestamp: '2026-05-04T00:00:00.000Z',

View file

@ -1,12 +1,12 @@
import { appendFile, mkdir } from 'node:fs/promises';
import { dirname } from 'node:path';
import type { ModelMessage } from 'ai';
import type { KloModelRole } from '@klo/llm';
import type { KtxModelRole } from '@ktx/llm';
type ProviderOptionsCarrier = { providerOptions?: unknown; [key: string]: unknown };
type ToolMap = Record<string, ProviderOptionsCarrier>;
export interface KloLlmDebugProviderOptionsEntry {
export interface KtxLlmDebugProviderOptionsEntry {
target: 'message' | 'message-part' | 'tool';
index?: number;
role?: string;
@ -15,29 +15,29 @@ export interface KloLlmDebugProviderOptionsEntry {
providerOptions: unknown;
}
export interface KloLlmDebugRequest {
export interface KtxLlmDebugRequest {
timestamp: string;
operationName: string;
source?: string;
jobId?: string;
unitKey?: string;
modelRole: KloModelRole;
modelRole: KtxModelRole;
modelId: string;
messageCount: number;
toolNames: string[];
providerOptions: KloLlmDebugProviderOptionsEntry[];
providerOptions: KtxLlmDebugProviderOptionsEntry[];
}
export interface KloLlmDebugRequestRecorder {
record(request: KloLlmDebugRequest): Promise<void> | void;
export interface KtxLlmDebugRequestRecorder {
record(request: KtxLlmDebugRequest): Promise<void> | void;
}
export interface SummarizeKloLlmDebugRequestInput {
export interface SummarizeKtxLlmDebugRequestInput {
operationName: string;
source?: string;
jobId?: string;
unitKey?: string;
modelRole: KloModelRole;
modelRole: KtxModelRole;
modelId: string;
messages: ModelMessage[];
tools: ToolMap;
@ -52,7 +52,7 @@ function isProviderOptionsCarrier(value: unknown): value is ProviderOptionsCarri
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function contentPartProviderOptions(message: ModelMessage, index: number): KloLlmDebugProviderOptionsEntry[] {
function contentPartProviderOptions(message: ModelMessage, index: number): KtxLlmDebugProviderOptionsEntry[] {
if (!Array.isArray(message.content)) {
return [];
}
@ -74,9 +74,9 @@ function contentPartProviderOptions(message: ModelMessage, index: number): KloLl
});
}
function messageProviderOptions(messages: ModelMessage[]): KloLlmDebugProviderOptionsEntry[] {
function messageProviderOptions(messages: ModelMessage[]): KtxLlmDebugProviderOptionsEntry[] {
return messages.flatMap((message, index) => {
const entries: KloLlmDebugProviderOptionsEntry[] = [];
const entries: KtxLlmDebugProviderOptionsEntry[] = [];
const providerOptions = (message as ProviderOptionsCarrier).providerOptions;
if (providerOptions) {
entries.push({
@ -91,7 +91,7 @@ function messageProviderOptions(messages: ModelMessage[]): KloLlmDebugProviderOp
});
}
function toolProviderOptions(tools: ToolMap): KloLlmDebugProviderOptionsEntry[] {
function toolProviderOptions(tools: ToolMap): KtxLlmDebugProviderOptionsEntry[] {
return Object.entries(tools).flatMap(([name, tool]) => {
return tool.providerOptions
? [
@ -105,7 +105,7 @@ function toolProviderOptions(tools: ToolMap): KloLlmDebugProviderOptionsEntry[]
});
}
export function summarizeKloLlmDebugRequest(input: SummarizeKloLlmDebugRequestInput): KloLlmDebugRequest {
export function summarizeKtxLlmDebugRequest(input: SummarizeKtxLlmDebugRequestInput): KtxLlmDebugRequest {
const toolNames = Object.keys(input.tools).sort();
return {
timestamp: input.timestamp ?? new Date().toISOString(),
@ -121,7 +121,7 @@ export function summarizeKloLlmDebugRequest(input: SummarizeKloLlmDebugRequestIn
};
}
export function createJsonlKloLlmDebugRequestRecorder(filePath: string): KloLlmDebugRequestRecorder {
export function createJsonlKtxLlmDebugRequestRecorder(filePath: string): KtxLlmDebugRequestRecorder {
return {
async record(request) {
await mkdir(dirname(filePath), { recursive: true });

View file

@ -1,8 +1,8 @@
import { describe, expect, it, vi } from 'vitest';
import { KloIngestEmbeddingPortAdapter, KloScanEmbeddingPortAdapter } from './embedding-port.js';
import { KtxIngestEmbeddingPortAdapter, KtxScanEmbeddingPortAdapter } from './embedding-port.js';
describe('KLO embedding port adapters', () => {
it('adapts @klo/llm embeddings to ingest embedding port shape', async () => {
describe('KTX embedding port adapters', () => {
it('adapts @ktx/llm embeddings to ingest embedding port shape', async () => {
const provider = {
dimensions: 3,
maxBatchSize: 2,
@ -12,7 +12,7 @@ describe('KLO embedding port adapters', () => {
[4, 5, 6],
]),
};
const adapter = new KloIngestEmbeddingPortAdapter(provider as never);
const adapter = new KtxIngestEmbeddingPortAdapter(provider as never);
await expect(adapter.computeEmbedding('alpha')).resolves.toEqual([1, 2, 3]);
await expect(adapter.computeEmbeddingsBulk(['alpha', 'beta'])).resolves.toEqual([
@ -22,14 +22,14 @@ describe('KLO embedding port adapters', () => {
expect(adapter.maxBatchSize).toBe(2);
});
it('adapts @klo/llm embeddings to scan embedding port shape', async () => {
it('adapts @ktx/llm embeddings to scan embedding port shape', async () => {
const provider = {
dimensions: 3,
maxBatchSize: 2,
embed: vi.fn(),
[['embed', 'Many'].join('')]: vi.fn(async () => [[1, 2, 3]]),
};
const adapter = new KloScanEmbeddingPortAdapter(provider as never);
const adapter = new KtxScanEmbeddingPortAdapter(provider as never);
await expect(adapter.embedBatch(['alpha'])).resolves.toEqual([[1, 2, 3]]);
expect(adapter.dimensions).toBe(3);

View file

@ -1,17 +1,17 @@
import type { KloEmbeddingProvider } from '@klo/llm';
import type { KloEmbeddingPort as KloIngestEmbeddingPort } from '../core/embedding.js';
import type { KloEmbeddingPort as KloScanEmbeddingPort } from '../scan/types.js';
import type { KtxEmbeddingProvider } from '@ktx/llm';
import type { KtxEmbeddingPort as KtxIngestEmbeddingPort } from '../core/embedding.js';
import type { KtxEmbeddingPort as KtxScanEmbeddingPort } from '../scan/types.js';
const bulkEmbeddingMethod = ['embed', 'Many'].join('') as keyof KloEmbeddingProvider;
const bulkEmbeddingMethod = ['embed', 'Many'].join('') as keyof KtxEmbeddingProvider;
function computeBulkEmbeddings(provider: KloEmbeddingProvider, texts: string[]): Promise<number[][]> {
function computeBulkEmbeddings(provider: KtxEmbeddingProvider, texts: string[]): Promise<number[][]> {
return (provider[bulkEmbeddingMethod] as (items: string[]) => Promise<number[][]>)(texts);
}
export class KloIngestEmbeddingPortAdapter implements KloIngestEmbeddingPort {
export class KtxIngestEmbeddingPortAdapter implements KtxIngestEmbeddingPort {
readonly maxBatchSize: number;
constructor(private readonly provider: KloEmbeddingProvider) {
constructor(private readonly provider: KtxEmbeddingProvider) {
this.maxBatchSize = provider.maxBatchSize;
}
@ -24,11 +24,11 @@ export class KloIngestEmbeddingPortAdapter implements KloIngestEmbeddingPort {
}
}
export class KloScanEmbeddingPortAdapter implements KloScanEmbeddingPort {
export class KtxScanEmbeddingPortAdapter implements KtxScanEmbeddingPort {
readonly dimensions: number;
readonly maxBatchSize: number;
constructor(private readonly provider: KloEmbeddingProvider) {
constructor(private readonly provider: KtxEmbeddingProvider) {
this.dimensions = provider.dimensions;
this.maxBatchSize = provider.maxBatchSize;
}

View file

@ -1,12 +1,12 @@
import { KloMessageBuilder, type KloLlmProvider, type KloModelRole } from '@klo/llm';
import { KtxMessageBuilder, type KtxLlmProvider, type KtxModelRole } from '@ktx/llm';
import { generateText, Output, type FlexibleSchema, type ToolSet } from 'ai';
type GenerateTextInput = Parameters<typeof generateText>[0];
type GenerateTextFn = (input: GenerateTextInput) => Promise<{ text?: string; output?: unknown }>;
interface GenerateKloTextInput {
llmProvider: KloLlmProvider;
role: KloModelRole;
interface GenerateKtxTextInput {
llmProvider: KtxLlmProvider;
role: KtxModelRole;
prompt: string;
system?: string;
tools?: ToolSet;
@ -14,12 +14,12 @@ interface GenerateKloTextInput {
generateText?: GenerateTextFn;
}
export async function generateKloText(input: GenerateKloTextInput): Promise<string> {
export async function generateKtxText(input: GenerateKtxTextInput): Promise<string> {
const model = input.llmProvider.getModel(input.role);
if ((model as { provider?: string }).provider === 'deterministic') {
return `Deterministic description for ${input.prompt.slice(0, 64).trim() || 'data source'}`;
}
const built = new KloMessageBuilder(input.llmProvider).wrapSimple({
const built = new KtxMessageBuilder(input.llmProvider).wrapSimple({
system: input.system,
messages: [{ role: 'user', content: input.prompt }],
tools: input.tools ?? {},
@ -32,16 +32,16 @@ export async function generateKloText(input: GenerateKloTextInput): Promise<stri
tools: built.tools as ToolSet,
});
if (typeof result.text !== 'string') {
throw new Error('KLO LLM text generation returned no text');
throw new Error('KTX LLM text generation returned no text');
}
return result.text;
}
export async function generateKloObject<TOutput, TSchema>(
input: GenerateKloTextInput & { schema: TSchema },
export async function generateKtxObject<TOutput, TSchema>(
input: GenerateKtxTextInput & { schema: TSchema },
): Promise<TOutput> {
const model = input.llmProvider.getModel(input.role);
const built = new KloMessageBuilder(input.llmProvider).wrapSimple({
const built = new KtxMessageBuilder(input.llmProvider).wrapSimple({
system: input.system,
messages: [{ role: 'user', content: input.prompt }],
tools: input.tools ?? {},
@ -57,7 +57,7 @@ export async function generateKloObject<TOutput, TSchema>(
}),
});
if (result.output == null) {
throw new Error('KLO LLM object generation returned no output');
throw new Error('KTX LLM object generation returned no output');
}
return result.output as TOutput;
}

View file

@ -1,18 +1,18 @@
export { KloIngestEmbeddingPortAdapter, KloScanEmbeddingPortAdapter } from './embedding-port.js';
export { generateKloObject, generateKloText } from './generation.js';
export { KtxIngestEmbeddingPortAdapter, KtxScanEmbeddingPortAdapter } from './embedding-port.js';
export { generateKtxObject, generateKtxText } from './generation.js';
export type {
KloLlmDebugProviderOptionsEntry,
KloLlmDebugRequest,
KloLlmDebugRequestRecorder,
SummarizeKloLlmDebugRequestInput,
KtxLlmDebugProviderOptionsEntry,
KtxLlmDebugRequest,
KtxLlmDebugRequestRecorder,
SummarizeKtxLlmDebugRequestInput,
} from './debug-request-recorder.js';
export {
createJsonlKloLlmDebugRequestRecorder,
summarizeKloLlmDebugRequest,
createJsonlKtxLlmDebugRequestRecorder,
summarizeKtxLlmDebugRequest,
} from './debug-request-recorder.js';
export {
createLocalKloEmbeddingProviderFromConfig,
createLocalKloLlmProviderFromConfig,
resolveLocalKloEmbeddingConfig,
resolveLocalKloLlmConfig,
createLocalKtxEmbeddingProviderFromConfig,
createLocalKtxLlmProviderFromConfig,
resolveLocalKtxEmbeddingConfig,
resolveLocalKtxLlmConfig,
} from './local-config.js';

View file

@ -1,31 +1,31 @@
import { describe, expect, it, vi } from 'vitest';
import {
buildDefaultKloProjectConfig,
type KloProjectEmbeddingConfig,
type KloProjectLlmConfig,
buildDefaultKtxProjectConfig,
type KtxProjectEmbeddingConfig,
type KtxProjectLlmConfig,
} from '../project/config.js';
import {
createLocalKloEmbeddingProviderFromConfig,
createLocalKloLlmProviderFromConfig,
resolveLocalKloEmbeddingConfig,
resolveLocalKloLlmConfig,
createLocalKtxEmbeddingProviderFromConfig,
createLocalKtxLlmProviderFromConfig,
resolveLocalKtxEmbeddingConfig,
resolveLocalKtxLlmConfig,
} from './local-config.js';
describe('local KLO LLM config', () => {
it('resolves env and file references into a KloLlmConfig', () => {
const config: KloProjectLlmConfig = {
describe('local KTX LLM config', () => {
it('resolves env and file references into a KtxLlmConfig', () => {
const config: KtxProjectLlmConfig = {
provider: {
backend: 'gateway',
gateway: { api_key: 'env:AI_GATEWAY_API_KEY', base_url: 'https://gateway.example/v1' }, // pragma: allowlist secret
},
models: { default: 'env:KLO_MODEL', triage: 'anthropic/claude-haiku-4-5' },
models: { default: 'env:KTX_MODEL', triage: 'anthropic/claude-haiku-4-5' },
promptCaching: { enabled: false },
};
expect(
resolveLocalKloLlmConfig(config, {
resolveLocalKtxLlmConfig(config, {
AI_GATEWAY_API_KEY: 'gateway-key', // pragma: allowlist secret
KLO_MODEL: 'anthropic/claude-sonnet-4-6',
KTX_MODEL: 'anthropic/claude-sonnet-4-6',
}),
).toEqual({
backend: 'gateway',
@ -37,16 +37,16 @@ describe('local KLO LLM config', () => {
it('returns null when the local LLM backend is disabled', () => {
expect(
createLocalKloLlmProviderFromConfig({
createLocalKtxLlmProviderFromConfig({
provider: { backend: 'none' },
models: {},
}),
).toBeNull();
});
it('constructs providers through @klo/llm', () => {
const createKloLlmProvider = vi.fn(() => ({ getModel: vi.fn() }) as never);
const result = createLocalKloLlmProviderFromConfig(
it('constructs providers through @ktx/llm', () => {
const createKtxLlmProvider = vi.fn(() => ({ getModel: vi.fn() }) as never);
const result = createLocalKtxLlmProviderFromConfig(
{
provider: {
backend: 'anthropic',
@ -54,11 +54,11 @@ describe('local KLO LLM config', () => {
},
models: { default: 'claude-sonnet-4-6' },
},
{ env: { ANTHROPIC_API_KEY: 'sk-ant-test' }, createKloLlmProvider }, // pragma: allowlist secret
{ env: { ANTHROPIC_API_KEY: 'sk-ant-test' }, createKtxLlmProvider }, // pragma: allowlist secret
);
expect(result).not.toBeNull();
expect(createKloLlmProvider).toHaveBeenCalledWith({
expect(createKtxLlmProvider).toHaveBeenCalledWith({
backend: 'anthropic',
anthropic: { apiKey: 'sk-ant-test' }, // pragma: allowlist secret
modelSlots: { default: 'claude-sonnet-4-6' },
@ -66,8 +66,8 @@ describe('local KLO LLM config', () => {
});
});
it('inherits enabled prompt caching from @klo/llm when local config omits promptCaching', () => {
const provider = createLocalKloLlmProviderFromConfig({
it('inherits enabled prompt caching from @ktx/llm when local config omits promptCaching', () => {
const provider = createLocalKtxLlmProviderFromConfig({
provider: {
backend: 'gateway',
gateway: { base_url: 'https://gateway.example/v1' },
@ -85,9 +85,9 @@ describe('local KLO LLM config', () => {
});
});
describe('local KLO embedding config', () => {
describe('local KTX embedding config', () => {
it('resolves sentence-transformers config', () => {
const config: KloProjectEmbeddingConfig = {
const config: KtxProjectEmbeddingConfig = {
backend: 'sentence-transformers',
model: 'all-MiniLM-L6-v2',
dimensions: 384,
@ -95,7 +95,7 @@ describe('local KLO embedding config', () => {
batchSize: 16,
};
expect(resolveLocalKloEmbeddingConfig(config, {})).toEqual({
expect(resolveLocalKtxEmbeddingConfig(config, {})).toEqual({
backend: 'sentence-transformers',
model: 'all-MiniLM-L6-v2',
dimensions: 384,
@ -105,14 +105,14 @@ describe('local KLO embedding config', () => {
});
it('constructs deterministic embeddings from the default project config', () => {
const createKloEmbeddingProvider = vi.fn(() => ({}) as never);
const provider = createLocalKloEmbeddingProviderFromConfig(
buildDefaultKloProjectConfig('warehouse').ingest.embeddings,
{ createKloEmbeddingProvider },
const createKtxEmbeddingProvider = vi.fn(() => ({}) as never);
const provider = createLocalKtxEmbeddingProviderFromConfig(
buildDefaultKtxProjectConfig('warehouse').ingest.embeddings,
{ createKtxEmbeddingProvider },
);
expect(provider).not.toBeNull();
expect(createKloEmbeddingProvider).toHaveBeenCalledWith(
expect(createKtxEmbeddingProvider).toHaveBeenCalledWith(
expect.objectContaining({
backend: 'deterministic',
model: 'deterministic',
@ -122,6 +122,6 @@ describe('local KLO embedding config', () => {
});
it('returns null when embeddings are disabled', () => {
expect(createLocalKloEmbeddingProviderFromConfig({ backend: 'none', dimensions: 8 })).toBeNull();
expect(createLocalKtxEmbeddingProviderFromConfig({ backend: 'none', dimensions: 8 })).toBeNull();
});
});

View file

@ -1,23 +1,23 @@
import {
createKloEmbeddingProvider,
createKloLlmProvider,
type KloEmbeddingConfig,
type KloEmbeddingProvider,
type KloLlmConfig,
type KloLlmProvider,
type KloModelRole,
} from '@klo/llm';
import { resolveKloConfigReference } from '../core/config-reference.js';
import type { KloProjectEmbeddingConfig, KloProjectLlmConfig } from '../project/config.js';
createKtxEmbeddingProvider,
createKtxLlmProvider,
type KtxEmbeddingConfig,
type KtxEmbeddingProvider,
type KtxLlmConfig,
type KtxLlmProvider,
type KtxModelRole,
} from '@ktx/llm';
import { resolveKtxConfigReference } from '../core/config-reference.js';
import type { KtxProjectEmbeddingConfig, KtxProjectLlmConfig } from '../project/config.js';
interface LocalConfigDeps {
env?: NodeJS.ProcessEnv;
createKloLlmProvider?: typeof createKloLlmProvider;
createKloEmbeddingProvider?: typeof createKloEmbeddingProvider;
createKtxLlmProvider?: typeof createKtxLlmProvider;
createKtxEmbeddingProvider?: typeof createKtxEmbeddingProvider;
}
function resolveOptional(value: string | undefined, env: NodeJS.ProcessEnv): string | undefined {
return resolveKloConfigReference(value, env) || undefined;
return resolveKtxConfigReference(value, env) || undefined;
}
function resolveRequired(value: string | undefined, env: NodeJS.ProcessEnv, message: string): string {
@ -29,19 +29,19 @@ function resolveRequired(value: string | undefined, env: NodeJS.ProcessEnv, mess
}
function resolveModelSlots(
models: KloProjectLlmConfig['models'],
models: KtxProjectLlmConfig['models'],
env: NodeJS.ProcessEnv,
): KloLlmConfig['modelSlots'] {
const resolved: Partial<Record<KloModelRole, string>> & { default?: string } = {};
): KtxLlmConfig['modelSlots'] {
const resolved: Partial<Record<KtxModelRole, string>> & { default?: string } = {};
for (const [role, value] of Object.entries(models)) {
if (value) {
resolved[role as KloModelRole] = resolveRequired(value, env, `llm.models.${role} is required`);
resolved[role as KtxModelRole] = resolveRequired(value, env, `llm.models.${role} is required`);
}
}
if (!resolved.default) {
throw new Error('llm.models.default is required when llm.provider.backend is not none');
}
return resolved as KloLlmConfig['modelSlots'];
return resolved as KtxLlmConfig['modelSlots'];
}
function resolvedProviderConfig(
@ -64,7 +64,7 @@ function resolvedProviderConfig(
};
}
export function resolveLocalKloLlmConfig(config: KloProjectLlmConfig, env: NodeJS.ProcessEnv): KloLlmConfig | null {
export function resolveLocalKtxLlmConfig(config: KtxProjectLlmConfig, env: NodeJS.ProcessEnv): KtxLlmConfig | null {
if (config.provider.backend === 'none') {
return null;
}
@ -81,18 +81,18 @@ export function resolveLocalKloLlmConfig(config: KloProjectLlmConfig, env: NodeJ
};
}
export function createLocalKloLlmProviderFromConfig(
config: KloProjectLlmConfig,
export function createLocalKtxLlmProviderFromConfig(
config: KtxProjectLlmConfig,
deps: LocalConfigDeps = {},
): KloLlmProvider | null {
const resolved = resolveLocalKloLlmConfig(config, deps.env ?? process.env);
return resolved ? (deps.createKloLlmProvider ?? createKloLlmProvider)(resolved) : null;
): KtxLlmProvider | null {
const resolved = resolveLocalKtxLlmConfig(config, deps.env ?? process.env);
return resolved ? (deps.createKtxLlmProvider ?? createKtxLlmProvider)(resolved) : null;
}
export function resolveLocalKloEmbeddingConfig(
config: KloProjectEmbeddingConfig,
export function resolveLocalKtxEmbeddingConfig(
config: KtxProjectEmbeddingConfig,
env: NodeJS.ProcessEnv,
): KloEmbeddingConfig | null {
): KtxEmbeddingConfig | null {
if (config.backend === 'none') {
return null;
}
@ -113,10 +113,10 @@ export function resolveLocalKloEmbeddingConfig(
};
}
export function createLocalKloEmbeddingProviderFromConfig(
config: KloProjectEmbeddingConfig,
export function createLocalKtxEmbeddingProviderFromConfig(
config: KtxProjectEmbeddingConfig,
deps: LocalConfigDeps = {},
): KloEmbeddingProvider | null {
const resolved = resolveLocalKloEmbeddingConfig(config, deps.env ?? process.env);
return resolved ? (deps.createKloEmbeddingProvider ?? createKloEmbeddingProvider)(resolved) : null;
): KtxEmbeddingProvider | null {
const resolved = resolveLocalKtxEmbeddingConfig(config, deps.env ?? process.env);
return resolved ? (deps.createKtxEmbeddingProvider ?? createKtxEmbeddingProvider)(resolved) : null;
}