fix: warn on claude-code prompt caching during setup

This commit is contained in:
Andrey Avtomonov 2026-05-15 16:34:53 +02:00
parent ea3c606b28
commit daca268fa8
4 changed files with 87 additions and 11 deletions

View file

@ -0,0 +1,28 @@
import type { KtxProjectLlmConfig } from '@ktx/context/project';
const CLAUDE_CODE_IGNORED_PROMPT_CACHING_FIELDS = [
'systemTtl',
'toolsTtl',
'historyTtl',
'vertexFallbackTo5m',
] as const;
export function ignoredClaudeCodePromptCachingFields(config: KtxProjectLlmConfig): string[] {
if (config.provider.backend !== 'claude-code' || !config.promptCaching) {
return [];
}
return CLAUDE_CODE_IGNORED_PROMPT_CACHING_FIELDS.filter((key) => key in config.promptCaching).map(
(key) => `llm.promptCaching.${key}`,
);
}
export function formatClaudeCodePromptCachingWarning(fields: string[]): string | null {
if (fields.length === 0) {
return null;
}
return `claude-code ignores ${fields.join(', ')} because the Claude Agent SDK does not expose KTX prompt-cache TTL, tool, or history markers.`;
}
export function formatClaudeCodePromptCachingFix(): string {
return 'Remove those promptCaching fields or use anthropic, vertex, or gateway when those cache knobs are required.';
}

View file

@ -209,6 +209,44 @@ describe('setup Anthropic model step', () => {
expect(authProbe).toHaveBeenCalledWith(expect.objectContaining({ projectDir: tempDir, model: 'sonnet' }));
});
it('warns during Claude Code setup when existing prompt-caching fields will be ignored', async () => {
await writeFile(
join(tempDir, 'ktx.yaml'),
[
'llm:',
' provider:',
' backend: anthropic',
' models:',
' default: claude-sonnet-4-6',
' promptCaching:',
' enabled: true',
' systemTtl: 1h',
' toolsTtl: 1h',
' historyTtl: 5m',
'',
].join('\n'),
'utf-8',
);
const io = makeIo();
const result = await runKtxSetupAnthropicModelStep(
{
projectDir: tempDir,
inputMode: 'disabled',
llmBackend: 'claude-code',
skipLlm: false,
},
io.io,
{
claudeCodeAuthProbe: async () => ({ ok: true as const }),
},
);
expect(result.status).toBe('ready');
expect(io.stderr()).toContain('claude-code ignores llm.promptCaching.systemTtl');
expect(io.stderr()).toContain('Claude Agent SDK does not expose KTX prompt-cache TTL, tool, or history markers');
});
it('returns from Anthropic credential Back to provider selection', async () => {
const prompts = makePromptAdapter({ selectValues: ['anthropic', 'back', 'back'] });

View file

@ -11,6 +11,10 @@ import {
serializeKtxProjectConfig,
} from '@ktx/context/project';
import { type KtxLlmConfig, type KtxLlmHealthCheckResult, runKtxLlmHealthCheck } from '@ktx/llm';
import {
formatClaudeCodePromptCachingWarning,
ignoredClaudeCodePromptCachingFields,
} from './claude-code-prompt-caching.js';
import { createClackSpinner, type KtxCliSpinner } from './clack.js';
import type { KtxCliIo } from './cli-runtime.js';
import { withTextInputNavigation } from './prompt-navigation.js';
@ -946,6 +950,14 @@ export async function runKtxSetupAnthropicModelStep(
io.stderr.write(`${health.message}\n`);
return { status: 'failed', projectDir: args.projectDir };
}
const warning = formatClaudeCodePromptCachingWarning(
ignoredClaudeCodePromptCachingFields(
buildProjectLlmConfig(project.config.llm, { backend: 'claude-code' }, model),
),
);
if (warning) {
io.stderr.write(`${warning}\n`);
}
await persistLlmConfig(args.projectDir, { backend: 'claude-code' }, model);
io.stdout.write(`│ LLM ready: yes (${model})\n`);
return { status: 'ready', projectDir: args.projectDir };

View file

@ -9,6 +9,11 @@ import type {
KtxProjectLlmConfig,
} from '@ktx/context/project';
import type { PostgresPgssProbeResult } from '@ktx/context/ingest';
import {
formatClaudeCodePromptCachingFix,
formatClaudeCodePromptCachingWarning,
ignoredClaudeCodePromptCachingFields,
} from './claude-code-prompt-caching.js';
import type { DoctorCheck } from './doctor.js';
import { KTX_NEXT_STEP_DIRECT_COMMANDS } from './next-steps.js';
@ -509,13 +514,6 @@ function buildPipelineStatus(config: KtxProjectConfig): PipelineStatus {
};
}
function ignoredClaudeCodePromptCachingFields(config: KtxProjectLlmConfig): string[] {
if (config.provider.backend !== 'claude-code' || !config.promptCaching) {
return [];
}
return Object.keys(config.promptCaching).map((key) => `llm.promptCaching.${key}`);
}
function buildStorageStatus(config: KtxProjectConfig): StorageStatus {
return {
state: config.storage.state,
@ -603,11 +601,11 @@ function buildWarnings(
});
}
const ignored = ignoredClaudeCodePromptCachingFields(config.llm);
if (ignored.length > 0) {
const warning = formatClaudeCodePromptCachingWarning(ignoredClaudeCodePromptCachingFields(config.llm));
if (warning) {
warnings.push({
message: `claude-code ignores ${ignored.join(', ')} because the Claude Agent SDK does not expose KTX prompt-cache TTL, tool, or history markers.`,
fix: 'Remove those promptCaching fields or use anthropic, vertex, or gateway when those cache knobs are required.',
message: warning,
fix: formatClaudeCodePromptCachingFix(),
});
}