@kaelio/ktx 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/python/{kaelio_ktx-0.9.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +17 -2
- package/dist/cli-program.d.ts +3 -0
- package/dist/cli-program.js +42 -2
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.js +44 -0
- package/dist/commands/setup-commands.js +2 -3
- package/dist/connection.js +23 -1
- package/dist/connectors/bigquery/connector.d.ts +2 -5
- package/dist/connectors/bigquery/connector.js +2 -2
- package/dist/connectors/clickhouse/connector.d.ts +2 -5
- package/dist/connectors/clickhouse/connector.js +2 -2
- package/dist/connectors/mysql/connector.d.ts +7 -6
- package/dist/connectors/mysql/connector.js +25 -5
- package/dist/connectors/mysql/dialect.d.ts +1 -1
- package/dist/connectors/mysql/dialect.js +12 -2
- package/dist/connectors/postgres/connector.d.ts +2 -5
- package/dist/connectors/postgres/connector.js +2 -2
- package/dist/connectors/snowflake/connector.d.ts +2 -5
- package/dist/connectors/snowflake/connector.js +2 -2
- package/dist/connectors/sqlite/connector.d.ts +2 -5
- package/dist/connectors/sqlite/connector.js +2 -2
- package/dist/connectors/sqlserver/connector.d.ts +2 -5
- package/dist/connectors/sqlserver/connector.js +2 -2
- package/dist/context/connections/drivers.d.ts +0 -1
- package/dist/context/connections/drivers.js +0 -7
- package/dist/context/connections/query-executor.d.ts +2 -1
- package/dist/context/core/abort.d.ts +9 -0
- package/dist/context/core/abort.js +36 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +1 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +6 -2
- package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
- package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
- package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
- package/dist/context/ingest/final-gate-repair.d.ts +1 -0
- package/dist/context/ingest/final-gate-repair.js +1 -0
- package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
- package/dist/context/ingest/ingest-bundle.runner.js +127 -53
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
- package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
- package/dist/context/ingest/local-bundle-runtime.js +11 -4
- package/dist/context/ingest/local-ingest.d.ts +1 -0
- package/dist/context/ingest/local-ingest.js +13 -3
- package/dist/context/ingest/memory-flow/events.js +1 -1
- package/dist/context/ingest/memory-flow/schema.js +8 -3
- package/dist/context/ingest/memory-flow/types.d.ts +7 -3
- package/dist/context/ingest/ports.d.ts +3 -5
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
- package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
- package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
- package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
- package/dist/context/ingest/types.d.ts +1 -0
- package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
- package/dist/context/llm/ai-sdk-runtime.js +152 -16
- package/dist/context/llm/claude-code-runtime.d.ts +6 -4
- package/dist/context/llm/claude-code-runtime.js +127 -48
- package/dist/context/llm/codex-runtime.d.ts +3 -3
- package/dist/context/llm/codex-runtime.js +90 -47
- package/dist/context/llm/local-config.d.ts +15 -5
- package/dist/context/llm/local-config.js +6 -1
- package/dist/context/llm/rate-limit-governor.d.ts +103 -0
- package/dist/context/llm/rate-limit-governor.js +285 -0
- package/dist/context/llm/runtime-port.d.ts +3 -6
- package/dist/context/mcp/context-tools.js +43 -13
- package/dist/context/project/config.d.ts +12 -0
- package/dist/context/project/config.js +35 -0
- package/dist/context/scan/types.d.ts +15 -2
- package/dist/context/scan/types.js +12 -0
- package/dist/context/sl/description-normalization.js +4 -14
- package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +60 -1
- package/dist/demo-metrics.d.ts +0 -2
- package/dist/demo-metrics.js +1 -11
- package/dist/ingest.d.ts +1 -0
- package/dist/ingest.js +32 -3
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/memory-flow-hud.js +8 -16
- package/dist/public-ingest.js +50 -15
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +18 -2
- package/dist/setup-databases.d.ts +1 -0
- package/dist/setup-databases.js +23 -3
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +1 -14
- package/dist/setup-models.js +116 -340
- package/dist/setup-prompts.js +3 -2
- package/dist/setup-sources.js +7 -7
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +1 -1
- package/dist/sl.d.ts +2 -2
- package/dist/sl.js +20 -4
- package/dist/sql.js +18 -2
- package/dist/star-prompt/cache.d.ts +16 -0
- package/dist/star-prompt/cache.js +45 -0
- package/dist/star-prompt/star-count.d.ts +7 -0
- package/dist/star-prompt/star-count.js +66 -0
- package/dist/star-prompt/star-line.d.ts +12 -0
- package/dist/star-prompt/star-line.js +26 -0
- package/dist/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +24 -0
- package/dist/telemetry/events.js +15 -0
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/index.d.ts +3 -2
- package/dist/telemetry/index.js +2 -1
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -0
- package/dist/update-check/cache.d.ts +21 -0
- package/dist/update-check/cache.js +38 -0
- package/dist/update-check/channel.d.ts +15 -0
- package/dist/update-check/channel.js +30 -0
- package/dist/update-check/registry.d.ts +1 -0
- package/dist/update-check/registry.js +45 -0
- package/dist/update-check/update-check.d.ts +43 -0
- package/dist/update-check/update-check.js +116 -0
- package/package.json +8 -1
- package/dist/context/connections/local-query-executor.d.ts +0 -6
- package/dist/context/connections/local-query-executor.js +0 -39
- package/dist/context/connections/postgres-query-executor.d.ts +0 -25
- package/dist/context/connections/postgres-query-executor.js +0 -53
- package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
- package/dist/context/connections/sqlite-query-executor.js +0 -74
package/dist/setup-demo-tour.js
CHANGED
package/dist/setup-embeddings.js
CHANGED
|
@@ -138,8 +138,8 @@ async function chooseCredentialRef(backend, args, io, deps) {
|
|
|
138
138
|
const choice = await prompts.select({
|
|
139
139
|
message: `How should KTX find your ${embeddingBackendDisplayName(backend)} embedding API key?`,
|
|
140
140
|
options: [
|
|
141
|
-
{ value: 'env', label: `Use ${defaultEnv} from the environment` },
|
|
142
141
|
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
|
|
142
|
+
{ value: 'env', label: `Use ${defaultEnv} from the environment` },
|
|
143
143
|
{ value: 'back', label: 'Back' },
|
|
144
144
|
],
|
|
145
145
|
});
|
package/dist/setup-models.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type KtxProjectLlmConfig } from './context/project/config.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type KtxLlmConfig } from './llm/types.js';
|
|
3
3
|
import { type KtxLlmHealthCheckResult } from './llm/model-health.js';
|
|
4
4
|
import { type KtxCliSpinner } from './clack.js';
|
|
5
5
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
@@ -10,7 +10,6 @@ export interface KtxSetupModelArgs {
|
|
|
10
10
|
llmBackend?: KtxSetupLlmBackend;
|
|
11
11
|
anthropicApiKeyEnv?: string;
|
|
12
12
|
anthropicApiKeyFile?: string;
|
|
13
|
-
llmModel?: string;
|
|
14
13
|
vertexProject?: string;
|
|
15
14
|
vertexLocation?: string;
|
|
16
15
|
forcePrompt?: boolean;
|
|
@@ -33,12 +32,6 @@ export type KtxSetupModelResult = {
|
|
|
33
32
|
status: 'failed';
|
|
34
33
|
projectDir: string;
|
|
35
34
|
};
|
|
36
|
-
/** @internal */
|
|
37
|
-
export interface AnthropicModelChoice {
|
|
38
|
-
id: string;
|
|
39
|
-
label: string;
|
|
40
|
-
recommended: boolean;
|
|
41
|
-
}
|
|
42
35
|
export type KtxSetupLlmBackend = 'anthropic' | 'vertex' | 'claude-code' | 'codex';
|
|
43
36
|
/** @internal */
|
|
44
37
|
export interface KtxSetupModelPromptAdapter {
|
|
@@ -62,9 +55,7 @@ export interface KtxSetupModelPromptAdapter {
|
|
|
62
55
|
}
|
|
63
56
|
export interface KtxSetupModelDeps {
|
|
64
57
|
env?: NodeJS.ProcessEnv;
|
|
65
|
-
fetch?: typeof fetch;
|
|
66
58
|
prompts?: KtxSetupModelPromptAdapter;
|
|
67
|
-
listModels?: (apiKey: string) => Promise<AnthropicModelChoice[]>;
|
|
68
59
|
healthCheck?: (config: KtxLlmConfig) => Promise<KtxLlmHealthCheckResult>;
|
|
69
60
|
claudeCodeAuthProbe?: (input: {
|
|
70
61
|
projectDir: string;
|
|
@@ -89,14 +80,10 @@ export interface KtxSetupModelDeps {
|
|
|
89
80
|
listGcloudProjects?: () => Promise<GcloudProjectChoice[]>;
|
|
90
81
|
spinner?: () => KtxCliSpinner;
|
|
91
82
|
}
|
|
92
|
-
/** @internal */
|
|
93
|
-
export declare const BUNDLED_ANTHROPIC_MODELS: AnthropicModelChoice[];
|
|
94
83
|
interface GcloudProjectChoice {
|
|
95
84
|
projectId: string;
|
|
96
85
|
name?: string;
|
|
97
86
|
}
|
|
98
|
-
/** @internal */
|
|
99
|
-
export declare function fetchAnthropicModels(apiKey: string, fetchFn?: typeof fetch): Promise<AnthropicModelChoice[]>;
|
|
100
87
|
export declare function isKtxSetupLlmConfigReady(config: KtxProjectLlmConfig): boolean;
|
|
101
88
|
export declare function runKtxSetupAnthropicModelStep(args: KtxSetupModelArgs, io: KtxCliIo, deps?: KtxSetupModelDeps): Promise<KtxSetupModelResult>;
|
|
102
89
|
export {};
|
package/dist/setup-models.js
CHANGED
|
@@ -10,6 +10,7 @@ import { resolveKtxConfigReference } from './context/core/config-reference.js';
|
|
|
10
10
|
import { serializeKtxProjectConfig } from './context/project/config.js';
|
|
11
11
|
import { loadKtxProject } from './context/project/project.js';
|
|
12
12
|
import { markKtxSetupStateStepComplete } from './context/project/setup-config.js';
|
|
13
|
+
import { KTX_MODEL_ROLES } from './llm/types.js';
|
|
13
14
|
import { runKtxLlmHealthCheck } from './llm/model-health.js';
|
|
14
15
|
import { formatClaudeCodePromptCachingWarning, ignoredClaudeCodePromptCachingFields, } from './claude-code-prompt-caching.js';
|
|
15
16
|
import { createClackSpinner } from './clack.js';
|
|
@@ -20,70 +21,46 @@ const ESC = String.fromCharCode(0x1b);
|
|
|
20
21
|
function yellow(text) {
|
|
21
22
|
return `${ESC}[33m${text}${ESC}[39m`;
|
|
22
23
|
}
|
|
23
|
-
/** @internal */
|
|
24
|
-
export const BUNDLED_ANTHROPIC_MODELS = [
|
|
25
|
-
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', recommended: true },
|
|
26
|
-
{ id: 'claude-opus-4-6', label: 'Claude Opus 4.6', recommended: false },
|
|
27
|
-
{ id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5', recommended: false },
|
|
28
|
-
];
|
|
29
|
-
const VERTEX_ANTHROPIC_MODELS = [
|
|
30
|
-
{ id: 'claude-opus-4-7', label: 'Claude Opus 4.7', recommended: false },
|
|
31
|
-
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', recommended: false },
|
|
32
|
-
{ id: 'claude-opus-4-6', label: 'Claude Opus 4.6', recommended: false },
|
|
33
|
-
{ id: 'claude-opus-4-5', label: 'Claude Opus 4.5', recommended: false },
|
|
34
|
-
{ id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5', recommended: false },
|
|
35
|
-
{ id: 'claude-sonnet-4-5', label: 'Claude Sonnet 4.5', recommended: false },
|
|
36
|
-
{ id: 'claude-opus-4-1', label: 'Claude Opus 4.1', recommended: false },
|
|
37
|
-
];
|
|
38
|
-
const CLAUDE_CODE_MODELS = [
|
|
39
|
-
{ id: 'sonnet', label: 'Claude Sonnet', recommended: true },
|
|
40
|
-
{ id: 'opus', label: 'Claude Opus', recommended: false },
|
|
41
|
-
{ id: 'haiku', label: 'Claude Haiku', recommended: false },
|
|
42
|
-
];
|
|
43
|
-
// Curated Codex models from OpenAI's current lineup that work under both
|
|
44
|
-
// ChatGPT-account (subscription) and API-key auth. Intentionally omitted:
|
|
45
|
-
// the `*-codex` ids (e.g. gpt-5.3-codex, gpt-5.2-codex) are API-key-only and
|
|
46
|
-
// fail on ChatGPT-account auth, and gpt-5.3-codex-spark is a ChatGPT-Pro-only
|
|
47
|
-
// research preview. Codex resolves real availability per account at runtime
|
|
48
|
-
// (its binary remote-fetches the model list), so this is a convenience
|
|
49
|
-
// shortlist only — the manual-entry option accepts any id your account's
|
|
50
|
-
// `codex` picker exposes, and the auth probe reports an unsupported choice.
|
|
51
|
-
const CODEX_MODELS = [
|
|
52
|
-
{ id: 'gpt-5.5', label: 'GPT-5.5', recommended: true },
|
|
53
|
-
{ id: 'gpt-5.4', label: 'GPT-5.4', recommended: false },
|
|
54
|
-
{ id: 'gpt-5.4-mini', label: 'GPT-5.4 mini', recommended: false },
|
|
55
|
-
];
|
|
56
|
-
const HIDDEN_ANTHROPIC_MODEL_PATTERNS = [
|
|
57
|
-
/^claude-sonnet-4$/i,
|
|
58
|
-
/^claude-opus-4$/i,
|
|
59
|
-
/^Claude Sonnet 4$/i,
|
|
60
|
-
/^Claude Opus 4$/i,
|
|
61
|
-
];
|
|
62
24
|
const ANTHROPIC_CREDENTIAL_PROMPT_CONTEXT = 'KTX uses the key to verify Anthropic model access now and to run ingest agents that turn schemas, SQL, ' +
|
|
63
25
|
'BI metadata, and docs into semantic-layer sources and wiki context. ktx.yaml stores an env: or file: ' +
|
|
64
26
|
'reference, not the raw key.';
|
|
65
|
-
const ANTHROPIC_MODEL_PROMPT_CONTEXT = 'KTX uses this as the default model for ingest agents that turn schemas, SQL, BI metadata, and docs ' +
|
|
66
|
-
'into semantic-layer sources and wiki context.';
|
|
67
27
|
const VERTEX_PROJECT_PROMPT_CONTEXT = 'KTX stores the selected Google Cloud project ID in ktx.yaml and uses Application Default Credentials for ' +
|
|
68
28
|
'access. Project visibility depends on the signed-in Google account and organization permissions.';
|
|
69
29
|
const DEFAULT_VERTEX_LOCATION = 'us-east5';
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
30
|
+
const ANTHROPIC_PRESET = {
|
|
31
|
+
default: 'claude-sonnet-4-6',
|
|
32
|
+
triage: 'claude-haiku-4-5',
|
|
33
|
+
candidateExtraction: 'claude-sonnet-4-6',
|
|
34
|
+
curator: 'claude-opus-4-7',
|
|
35
|
+
reconcile: 'claude-opus-4-7',
|
|
36
|
+
repair: 'claude-haiku-4-5',
|
|
37
|
+
};
|
|
38
|
+
const CLAUDE_CODE_PRESET = {
|
|
39
|
+
default: 'sonnet',
|
|
40
|
+
triage: 'haiku',
|
|
41
|
+
candidateExtraction: 'sonnet',
|
|
42
|
+
curator: 'opus',
|
|
43
|
+
reconcile: 'opus',
|
|
44
|
+
repair: 'haiku',
|
|
45
|
+
};
|
|
46
|
+
const CODEX_PRESET = {
|
|
47
|
+
default: DEFAULT_CODEX_MODEL,
|
|
48
|
+
triage: DEFAULT_CODEX_MODEL,
|
|
49
|
+
candidateExtraction: DEFAULT_CODEX_MODEL,
|
|
50
|
+
curator: DEFAULT_CODEX_MODEL,
|
|
51
|
+
reconcile: DEFAULT_CODEX_MODEL,
|
|
52
|
+
repair: DEFAULT_CODEX_MODEL,
|
|
53
|
+
};
|
|
54
|
+
const MODEL_PRESETS = {
|
|
55
|
+
anthropic: ANTHROPIC_PRESET,
|
|
56
|
+
vertex: ANTHROPIC_PRESET,
|
|
57
|
+
'claude-code': CLAUDE_CODE_PRESET,
|
|
58
|
+
codex: CODEX_PRESET,
|
|
59
|
+
};
|
|
60
|
+
function presetForBackend(backend) {
|
|
61
|
+
return MODEL_PRESETS[backend];
|
|
86
62
|
}
|
|
63
|
+
const execFileAsync = promisify(execFile);
|
|
87
64
|
function createPromptAdapter() {
|
|
88
65
|
return createKtxSetupPromptAdapter({ selectCancelValue: 'back' });
|
|
89
66
|
}
|
|
@@ -122,35 +99,6 @@ async function defaultListGcloudProjects() {
|
|
|
122
99
|
})
|
|
123
100
|
.filter((project) => Boolean(project));
|
|
124
101
|
}
|
|
125
|
-
/** @internal */
|
|
126
|
-
export async function fetchAnthropicModels(apiKey, fetchFn = fetch) {
|
|
127
|
-
const response = await fetchFn('https://api.anthropic.com/v1/models?limit=1000', {
|
|
128
|
-
headers: {
|
|
129
|
-
'anthropic-version': '2023-06-01',
|
|
130
|
-
'x-api-key': apiKey,
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
if (!response.ok) {
|
|
134
|
-
if (response.status === 401 || response.status === 403) {
|
|
135
|
-
throw new AnthropicModelDiscoveryError(`Anthropic model discovery failed with HTTP ${response.status}`, 'authentication', response.status);
|
|
136
|
-
}
|
|
137
|
-
throw new AnthropicModelDiscoveryError(`Anthropic model discovery failed with HTTP ${response.status}`, 'http', response.status);
|
|
138
|
-
}
|
|
139
|
-
const body = (await response.json());
|
|
140
|
-
const models = (body.data ?? [])
|
|
141
|
-
.map((item) => ({
|
|
142
|
-
id: typeof item.id === 'string' ? item.id : '',
|
|
143
|
-
label: typeof item.display_name === 'string' ? item.display_name : typeof item.id === 'string' ? item.id : '',
|
|
144
|
-
recommended: false,
|
|
145
|
-
}))
|
|
146
|
-
.filter((item) => item.id.startsWith('claude-'))
|
|
147
|
-
.filter(isSelectableAnthropicModel);
|
|
148
|
-
if (models.length === 0) {
|
|
149
|
-
throw new AnthropicModelDiscoveryError('Anthropic model discovery returned no Claude models', 'empty-response');
|
|
150
|
-
}
|
|
151
|
-
const recommendedIndex = models.findIndex((item) => item.id.includes('sonnet'));
|
|
152
|
-
return models.map((item, index) => ({ ...item, recommended: index === Math.max(recommendedIndex, 0) }));
|
|
153
|
-
}
|
|
154
102
|
export function isKtxSetupLlmConfigReady(config) {
|
|
155
103
|
let resolved;
|
|
156
104
|
try {
|
|
@@ -173,18 +121,18 @@ export function isKtxSetupLlmConfigReady(config) {
|
|
|
173
121
|
function hasUsableConfiguredLlm(config) {
|
|
174
122
|
return isKtxSetupLlmConfigReady(config.llm);
|
|
175
123
|
}
|
|
176
|
-
function buildProjectLlmConfig(existing, provider,
|
|
124
|
+
function buildProjectLlmConfig(existing, provider, models) {
|
|
177
125
|
if (provider.backend === 'claude-code') {
|
|
178
126
|
return {
|
|
179
127
|
provider: { backend: 'claude-code' },
|
|
180
|
-
models
|
|
128
|
+
models,
|
|
181
129
|
promptCaching: existing.promptCaching,
|
|
182
130
|
};
|
|
183
131
|
}
|
|
184
132
|
if (provider.backend === 'codex') {
|
|
185
133
|
return {
|
|
186
134
|
provider: { backend: 'codex' },
|
|
187
|
-
models
|
|
135
|
+
models,
|
|
188
136
|
promptCaching: existing.promptCaching,
|
|
189
137
|
};
|
|
190
138
|
}
|
|
@@ -194,7 +142,7 @@ function buildProjectLlmConfig(existing, provider, model) {
|
|
|
194
142
|
backend: 'vertex',
|
|
195
143
|
vertex: provider.vertex,
|
|
196
144
|
},
|
|
197
|
-
models
|
|
145
|
+
models,
|
|
198
146
|
promptCaching: { ...(existing.promptCaching ?? {}), enabled: true, vertexFallbackTo5m: true },
|
|
199
147
|
};
|
|
200
148
|
}
|
|
@@ -203,7 +151,7 @@ function buildProjectLlmConfig(existing, provider, model) {
|
|
|
203
151
|
backend: 'anthropic',
|
|
204
152
|
anthropic: { api_key: provider.credentialRef },
|
|
205
153
|
},
|
|
206
|
-
models
|
|
154
|
+
models,
|
|
207
155
|
promptCaching: { ...(existing.promptCaching ?? {}), enabled: true },
|
|
208
156
|
};
|
|
209
157
|
}
|
|
@@ -302,8 +250,8 @@ async function chooseCredentialRef(args, io, deps) {
|
|
|
302
250
|
const choice = await prompts.select({
|
|
303
251
|
message: `How should KTX find your Anthropic API key?\n\n${ANTHROPIC_CREDENTIAL_PROMPT_CONTEXT}`,
|
|
304
252
|
options: [
|
|
305
|
-
{ value: 'env', label: 'Use ANTHROPIC_API_KEY from the environment' },
|
|
306
253
|
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
|
|
254
|
+
{ value: 'env', label: 'Use ANTHROPIC_API_KEY from the environment' },
|
|
307
255
|
{ value: 'back', label: 'Back' },
|
|
308
256
|
],
|
|
309
257
|
});
|
|
@@ -342,14 +290,11 @@ function requestedBackend(args) {
|
|
|
342
290
|
if (args.vertexProject || args.vertexLocation) {
|
|
343
291
|
return 'vertex';
|
|
344
292
|
}
|
|
345
|
-
if (args.anthropicApiKeyEnv || args.anthropicApiKeyFile
|
|
293
|
+
if (args.anthropicApiKeyEnv || args.anthropicApiKeyFile) {
|
|
346
294
|
return 'anthropic';
|
|
347
295
|
}
|
|
348
296
|
return undefined;
|
|
349
297
|
}
|
|
350
|
-
function requestedModel(args) {
|
|
351
|
-
return args.llmModel;
|
|
352
|
-
}
|
|
353
298
|
async function chooseBackend(args, io, deps) {
|
|
354
299
|
const explicit = requestedBackend(args);
|
|
355
300
|
if (explicit) {
|
|
@@ -568,177 +513,11 @@ async function chooseVertexConfig(args, io, deps) {
|
|
|
568
513
|
},
|
|
569
514
|
};
|
|
570
515
|
}
|
|
571
|
-
async function
|
|
572
|
-
const providedModel = requestedModel(args);
|
|
573
|
-
if (providedModel) {
|
|
574
|
-
return { status: 'ready', model: providedModel };
|
|
575
|
-
}
|
|
576
|
-
if (args.inputMode === 'disabled') {
|
|
577
|
-
io.stderr.write('Missing LLM model: pass --llm-model.\n');
|
|
578
|
-
return { status: 'missing-input' };
|
|
579
|
-
}
|
|
580
|
-
let models;
|
|
581
|
-
try {
|
|
582
|
-
models = deps.listModels
|
|
583
|
-
? await deps.listModels(credentialValue)
|
|
584
|
-
: await fetchAnthropicModels(credentialValue, deps.fetch);
|
|
585
|
-
}
|
|
586
|
-
catch (error) {
|
|
587
|
-
if (isAnthropicModelAuthenticationError(error)) {
|
|
588
|
-
const statusSuffix = error.status ? ` (HTTP ${error.status})` : '';
|
|
589
|
-
io.stderr.write(`Anthropic API key is invalid or unauthorized${statusSuffix}. Check the key and try again.\n`);
|
|
590
|
-
return { status: 'invalid-credential' };
|
|
591
|
-
}
|
|
592
|
-
io.stderr.write('Could not fetch live Anthropic models. Showing bundled defaults. Setup will still test the selected model before saving it.\n');
|
|
593
|
-
models = BUNDLED_ANTHROPIC_MODELS;
|
|
594
|
-
}
|
|
595
|
-
const selectableModels = models.filter(isSelectableAnthropicModel);
|
|
596
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
597
|
-
const modelOptions = [
|
|
598
|
-
...selectableModels.map((model) => ({
|
|
599
|
-
value: model.id,
|
|
600
|
-
label: model.label || model.id,
|
|
601
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
602
|
-
})),
|
|
603
|
-
{ value: 'manual', label: 'Enter a model ID manually' },
|
|
604
|
-
{ value: 'back', label: 'Back' },
|
|
605
|
-
];
|
|
606
|
-
const choice = await prompts.autocomplete({
|
|
607
|
-
message: `Which Anthropic model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
608
|
-
placeholder: 'Type to search models',
|
|
609
|
-
options: modelOptions,
|
|
610
|
-
});
|
|
611
|
-
if (choice === 'back') {
|
|
612
|
-
return { status: 'back' };
|
|
613
|
-
}
|
|
614
|
-
if (choice === 'manual') {
|
|
615
|
-
const manual = await prompts.text({
|
|
616
|
-
message: withTextInputNavigation('Anthropic model ID'),
|
|
617
|
-
placeholder: selectableModels.find((model) => model.recommended)?.id ?? selectableModels[0]?.id,
|
|
618
|
-
});
|
|
619
|
-
if (manual === undefined) {
|
|
620
|
-
return { status: 'back' };
|
|
621
|
-
}
|
|
622
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
623
|
-
}
|
|
624
|
-
return { status: 'ready', model: choice };
|
|
625
|
-
}
|
|
626
|
-
async function chooseVertexModel(args, io, deps) {
|
|
627
|
-
const providedModel = requestedModel(args);
|
|
628
|
-
if (providedModel) {
|
|
629
|
-
return { status: 'ready', model: providedModel };
|
|
630
|
-
}
|
|
631
|
-
if (args.inputMode === 'disabled') {
|
|
632
|
-
io.stderr.write('Missing LLM model: pass --llm-model.\n');
|
|
633
|
-
return { status: 'missing-input' };
|
|
634
|
-
}
|
|
635
|
-
const selectableModels = VERTEX_ANTHROPIC_MODELS.filter(isSelectableAnthropicModel);
|
|
636
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
637
|
-
const choice = await prompts.autocomplete({
|
|
638
|
-
message: `Which Anthropic model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
639
|
-
placeholder: 'Type to search models',
|
|
640
|
-
options: [
|
|
641
|
-
...selectableModels.map((model) => ({
|
|
642
|
-
value: model.id,
|
|
643
|
-
label: model.label || model.id,
|
|
644
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
645
|
-
})),
|
|
646
|
-
{ value: 'manual', label: 'Enter a model ID manually' },
|
|
647
|
-
{ value: 'back', label: 'Back' },
|
|
648
|
-
],
|
|
649
|
-
});
|
|
650
|
-
if (choice === 'back') {
|
|
651
|
-
return { status: 'back' };
|
|
652
|
-
}
|
|
653
|
-
if (choice === 'manual') {
|
|
654
|
-
const manual = await prompts.text({
|
|
655
|
-
message: withTextInputNavigation('Anthropic model ID'),
|
|
656
|
-
placeholder: selectableModels.find((model) => model.recommended)?.id ?? selectableModels[0]?.id,
|
|
657
|
-
});
|
|
658
|
-
if (manual === undefined) {
|
|
659
|
-
return { status: 'back' };
|
|
660
|
-
}
|
|
661
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
662
|
-
}
|
|
663
|
-
return { status: 'ready', model: choice };
|
|
664
|
-
}
|
|
665
|
-
async function chooseClaudeCodeModel(args, deps) {
|
|
666
|
-
const providedModel = requestedModel(args);
|
|
667
|
-
if (providedModel) {
|
|
668
|
-
return { status: 'ready', model: providedModel };
|
|
669
|
-
}
|
|
670
|
-
if (args.inputMode === 'disabled') {
|
|
671
|
-
return { status: 'ready', model: 'sonnet' };
|
|
672
|
-
}
|
|
673
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
674
|
-
const choice = await prompts.select({
|
|
675
|
-
message: `Which Claude Code model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
676
|
-
options: [
|
|
677
|
-
...CLAUDE_CODE_MODELS.map((model) => ({
|
|
678
|
-
value: model.id,
|
|
679
|
-
label: model.label,
|
|
680
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
681
|
-
})),
|
|
682
|
-
{ value: 'manual', label: 'Enter a Claude Code model ID manually' },
|
|
683
|
-
{ value: 'back', label: 'Back' },
|
|
684
|
-
],
|
|
685
|
-
});
|
|
686
|
-
if (choice === 'back') {
|
|
687
|
-
return { status: 'back' };
|
|
688
|
-
}
|
|
689
|
-
if (choice === 'manual') {
|
|
690
|
-
const manual = await prompts.text({
|
|
691
|
-
message: withTextInputNavigation('Claude Code model ID'),
|
|
692
|
-
placeholder: CLAUDE_CODE_MODELS.find((model) => model.recommended)?.id ?? CLAUDE_CODE_MODELS[0]?.id,
|
|
693
|
-
});
|
|
694
|
-
if (manual === undefined) {
|
|
695
|
-
return { status: 'back' };
|
|
696
|
-
}
|
|
697
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
698
|
-
}
|
|
699
|
-
return { status: 'ready', model: choice };
|
|
700
|
-
}
|
|
701
|
-
async function chooseCodexModel(args, deps) {
|
|
702
|
-
const providedModel = requestedModel(args);
|
|
703
|
-
if (providedModel) {
|
|
704
|
-
return { status: 'ready', model: providedModel };
|
|
705
|
-
}
|
|
706
|
-
if (args.inputMode === 'disabled') {
|
|
707
|
-
return { status: 'ready', model: DEFAULT_CODEX_MODEL };
|
|
708
|
-
}
|
|
709
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
710
|
-
const choice = await prompts.select({
|
|
711
|
-
message: `Which Codex model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
712
|
-
options: [
|
|
713
|
-
...CODEX_MODELS.map((model) => ({
|
|
714
|
-
value: model.id,
|
|
715
|
-
label: model.label,
|
|
716
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
717
|
-
})),
|
|
718
|
-
{ value: 'manual', label: 'Enter a Codex model ID manually' },
|
|
719
|
-
{ value: 'back', label: 'Back' },
|
|
720
|
-
],
|
|
721
|
-
});
|
|
722
|
-
if (choice === 'back') {
|
|
723
|
-
return { status: 'back' };
|
|
724
|
-
}
|
|
725
|
-
if (choice === 'manual') {
|
|
726
|
-
const manual = await prompts.text({
|
|
727
|
-
message: withTextInputNavigation('Codex model ID'),
|
|
728
|
-
placeholder: CODEX_MODELS.find((model) => model.recommended)?.id ?? CODEX_MODELS[0]?.id,
|
|
729
|
-
});
|
|
730
|
-
if (manual === undefined) {
|
|
731
|
-
return { status: 'back' };
|
|
732
|
-
}
|
|
733
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
734
|
-
}
|
|
735
|
-
return { status: 'ready', model: choice };
|
|
736
|
-
}
|
|
737
|
-
async function persistLlmConfig(projectDir, provider, model) {
|
|
516
|
+
async function persistLlmConfig(projectDir, provider, models) {
|
|
738
517
|
const project = await loadKtxProject({ projectDir });
|
|
739
518
|
const config = {
|
|
740
519
|
...project.config,
|
|
741
|
-
llm: buildProjectLlmConfig(project.config.llm, provider,
|
|
520
|
+
llm: buildProjectLlmConfig(project.config.llm, provider, models),
|
|
742
521
|
scan: {
|
|
743
522
|
...project.config.scan,
|
|
744
523
|
enrichment: {
|
|
@@ -759,6 +538,48 @@ function buildInteractiveRetryArgs(args, backend) {
|
|
|
759
538
|
skipLlm: args.skipLlm,
|
|
760
539
|
};
|
|
761
540
|
}
|
|
541
|
+
function distinctPresetModels(preset) {
|
|
542
|
+
const models = [];
|
|
543
|
+
const seen = new Set();
|
|
544
|
+
for (const role of KTX_MODEL_ROLES) {
|
|
545
|
+
const model = preset[role];
|
|
546
|
+
if (!seen.has(model)) {
|
|
547
|
+
seen.add(model);
|
|
548
|
+
models.push(model);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return models;
|
|
552
|
+
}
|
|
553
|
+
function rolesUsingModel(preset, model) {
|
|
554
|
+
return KTX_MODEL_ROLES.filter((role) => preset[role] === model);
|
|
555
|
+
}
|
|
556
|
+
function formatPresetFallbackWarning(roles, unavailableModel, anchorModel) {
|
|
557
|
+
return `LLM model ${unavailableModel} is unavailable for ${roles.join(', ')}; using ${anchorModel} for those roles.`;
|
|
558
|
+
}
|
|
559
|
+
async function validatePresetModels(preset, validateModel, io) {
|
|
560
|
+
const anchorModel = preset.default;
|
|
561
|
+
const degraded = { ...preset };
|
|
562
|
+
const models = distinctPresetModels(preset);
|
|
563
|
+
const anchorResult = await validateModel(anchorModel);
|
|
564
|
+
if (!anchorResult.ok) {
|
|
565
|
+
return { status: 'failed', message: anchorResult.message };
|
|
566
|
+
}
|
|
567
|
+
for (const model of models) {
|
|
568
|
+
if (model === anchorModel) {
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
const result = await validateModel(model);
|
|
572
|
+
if (result.ok) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
const affectedRoles = rolesUsingModel(degraded, model);
|
|
576
|
+
for (const role of affectedRoles) {
|
|
577
|
+
degraded[role] = anchorModel;
|
|
578
|
+
}
|
|
579
|
+
io.stderr.write(`${formatPresetFallbackWarning(affectedRoles, model, anchorModel)}\n`);
|
|
580
|
+
}
|
|
581
|
+
return { status: 'ready', models: degraded };
|
|
582
|
+
}
|
|
762
583
|
export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
763
584
|
if (args.skipLlm) {
|
|
764
585
|
io.stdout.write('│ LLM setup skipped.\n');
|
|
@@ -770,7 +591,6 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
770
591
|
!args.llmBackend &&
|
|
771
592
|
!args.anthropicApiKeyEnv &&
|
|
772
593
|
!args.anthropicApiKeyFile &&
|
|
773
|
-
!args.llmModel &&
|
|
774
594
|
!args.vertexProject &&
|
|
775
595
|
!args.vertexLocation) {
|
|
776
596
|
io.stdout.write(`│ LLM ready: yes (${project.config.llm.models.default})\n`);
|
|
@@ -795,80 +615,50 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
795
615
|
if (vertex.status !== 'ready') {
|
|
796
616
|
return { status: vertex.status, projectDir: args.projectDir };
|
|
797
617
|
}
|
|
798
|
-
const
|
|
799
|
-
|
|
618
|
+
const preset = presetForBackend('vertex');
|
|
619
|
+
const validation = await validatePresetModels(preset, async (model) => runLlmHealthCheckWithProgress(buildVertexHealthConfig(vertex.values, model), 'Vertex AI', model, healthCheck, deps), io);
|
|
620
|
+
if (validation.status !== 'ready') {
|
|
621
|
+
io.stderr.write(`Vertex AI Anthropic model health check failed: ${formatVertexHealthFailure(validation.message, vertex.values)}\n`);
|
|
622
|
+
if (args.inputMode === 'disabled') {
|
|
623
|
+
return { status: 'failed', projectDir: args.projectDir };
|
|
624
|
+
}
|
|
625
|
+
io.stderr.write('Choose a different Vertex AI project or location, or Back.\n');
|
|
800
626
|
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
801
627
|
continue;
|
|
802
628
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
if (model.status !== 'ready') {
|
|
807
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
808
|
-
}
|
|
809
|
-
const health = await runLlmHealthCheckWithProgress(buildVertexHealthConfig(vertex.values, model.model), 'Vertex AI', model.model, healthCheck, deps);
|
|
810
|
-
if (health.ok) {
|
|
811
|
-
await persistLlmConfig(args.projectDir, { backend: 'vertex', vertex: vertex.refs }, model.model);
|
|
812
|
-
io.stdout.write(`│ LLM ready: yes (${model.model})\n`);
|
|
813
|
-
return { status: 'ready', projectDir: args.projectDir };
|
|
814
|
-
}
|
|
815
|
-
io.stderr.write(`Vertex AI Anthropic model health check failed: ${formatVertexHealthFailure(health.message, vertex.values)}\n`);
|
|
816
|
-
if (args.inputMode === 'disabled') {
|
|
817
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
818
|
-
}
|
|
819
|
-
io.stderr.write('Choose a different Vertex AI project, location, or model, or Back.\n');
|
|
820
|
-
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
821
|
-
continue;
|
|
629
|
+
await persistLlmConfig(args.projectDir, { backend: 'vertex', vertex: vertex.refs }, validation.models);
|
|
630
|
+
io.stdout.write(`│ LLM ready: yes (${validation.models.default})\n`);
|
|
631
|
+
return { status: 'ready', projectDir: args.projectDir };
|
|
822
632
|
}
|
|
823
633
|
if (backendChoice.backend === 'claude-code') {
|
|
824
|
-
const
|
|
825
|
-
if (model.status === 'back' && backendChoice.prompted) {
|
|
826
|
-
attemptArgs = buildInteractiveRetryArgs(args);
|
|
827
|
-
continue;
|
|
828
|
-
}
|
|
829
|
-
if (model.status === 'invalid-credential') {
|
|
830
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
831
|
-
}
|
|
832
|
-
if (model.status !== 'ready') {
|
|
833
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
834
|
-
}
|
|
634
|
+
const preset = presetForBackend('claude-code');
|
|
835
635
|
const probe = deps.claudeCodeAuthProbe ?? runClaudeCodeAuthProbe;
|
|
836
|
-
const
|
|
837
|
-
if (
|
|
838
|
-
io.stderr.write(`${
|
|
636
|
+
const validation = await validatePresetModels(preset, async (model) => probe({ projectDir: args.projectDir, model, env: deps.env ?? process.env }), io);
|
|
637
|
+
if (validation.status !== 'ready') {
|
|
638
|
+
io.stderr.write(`${validation.message}\n`);
|
|
839
639
|
return { status: 'failed', projectDir: args.projectDir };
|
|
840
640
|
}
|
|
841
|
-
const warning = formatClaudeCodePromptCachingWarning(ignoredClaudeCodePromptCachingFields(buildProjectLlmConfig(project.config.llm, { backend: 'claude-code' },
|
|
641
|
+
const warning = formatClaudeCodePromptCachingWarning(ignoredClaudeCodePromptCachingFields(buildProjectLlmConfig(project.config.llm, { backend: 'claude-code' }, validation.models)));
|
|
842
642
|
if (warning) {
|
|
843
643
|
io.stderr.write(`${warning}\n`);
|
|
844
644
|
}
|
|
845
|
-
await persistLlmConfig(args.projectDir, { backend: 'claude-code' },
|
|
846
|
-
io.stdout.write(`│ LLM ready: yes (${
|
|
645
|
+
await persistLlmConfig(args.projectDir, { backend: 'claude-code' }, validation.models);
|
|
646
|
+
io.stdout.write(`│ LLM ready: yes (${validation.models.default})\n`);
|
|
847
647
|
return { status: 'ready', projectDir: args.projectDir };
|
|
848
648
|
}
|
|
849
649
|
if (backendChoice.backend === 'codex') {
|
|
850
|
-
const
|
|
851
|
-
if (model.status === 'back' && backendChoice.prompted) {
|
|
852
|
-
attemptArgs = buildInteractiveRetryArgs(args);
|
|
853
|
-
continue;
|
|
854
|
-
}
|
|
855
|
-
if (model.status === 'invalid-credential') {
|
|
856
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
857
|
-
}
|
|
858
|
-
if (model.status !== 'ready') {
|
|
859
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
860
|
-
}
|
|
650
|
+
const preset = presetForBackend('codex');
|
|
861
651
|
const probe = deps.codexAuthProbe ?? runCodexAuthProbe;
|
|
862
|
-
const
|
|
863
|
-
if (
|
|
864
|
-
io.stderr.write(`${
|
|
652
|
+
const validation = await validatePresetModels(preset, async (model) => probe({ projectDir: args.projectDir, model }), io);
|
|
653
|
+
if (validation.status !== 'ready') {
|
|
654
|
+
io.stderr.write(`${validation.message}\n`);
|
|
865
655
|
return { status: 'failed', projectDir: args.projectDir };
|
|
866
656
|
}
|
|
867
657
|
// Prefix the clack gutter so the warning sits inside the setup frame
|
|
868
658
|
// instead of breaking out of it; kept on stderr for scripted runs.
|
|
869
659
|
io.stderr.write(`│ ${formatCodexIsolationWarning()}\n`);
|
|
870
|
-
await persistLlmConfig(args.projectDir, { backend: 'codex' },
|
|
871
|
-
io.stdout.write(`│ LLM ready: yes (codex, ${
|
|
660
|
+
await persistLlmConfig(args.projectDir, { backend: 'codex' }, validation.models);
|
|
661
|
+
io.stdout.write(`│ LLM ready: yes (codex, ${validation.models.default})\n`);
|
|
872
662
|
return { status: 'ready', projectDir: args.projectDir };
|
|
873
663
|
}
|
|
874
664
|
const credential = await chooseCredentialRef(backendArgs, io, deps);
|
|
@@ -879,8 +669,10 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
879
669
|
if (credential.status !== 'ready') {
|
|
880
670
|
return { status: credential.status, projectDir: args.projectDir };
|
|
881
671
|
}
|
|
882
|
-
const
|
|
883
|
-
|
|
672
|
+
const preset = presetForBackend('anthropic');
|
|
673
|
+
const validation = await validatePresetModels(preset, async (model) => runLlmHealthCheckWithProgress(buildAnthropicHealthConfig(credential.value, model), 'Anthropic API', model, healthCheck, deps), io);
|
|
674
|
+
if (validation.status !== 'ready') {
|
|
675
|
+
io.stderr.write(`Anthropic model health check failed: ${validation.message}\n`);
|
|
884
676
|
if (args.inputMode === 'disabled') {
|
|
885
677
|
return { status: 'failed', projectDir: args.projectDir };
|
|
886
678
|
}
|
|
@@ -888,24 +680,8 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
888
680
|
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
889
681
|
continue;
|
|
890
682
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
if (model.status !== 'ready') {
|
|
896
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
897
|
-
}
|
|
898
|
-
const health = await runLlmHealthCheckWithProgress(buildAnthropicHealthConfig(credential.value, model.model), 'Anthropic API', model.model, healthCheck, deps);
|
|
899
|
-
if (health.ok) {
|
|
900
|
-
await persistLlmConfig(args.projectDir, { backend: 'anthropic', credentialRef: credential.ref }, model.model);
|
|
901
|
-
io.stdout.write(`│ LLM ready: yes (${model.model})\n`);
|
|
902
|
-
return { status: 'ready', projectDir: args.projectDir };
|
|
903
|
-
}
|
|
904
|
-
io.stderr.write(`Anthropic model health check failed: ${health.message}\n`);
|
|
905
|
-
if (args.inputMode === 'disabled') {
|
|
906
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
907
|
-
}
|
|
908
|
-
io.stderr.write('Choose a different credential source or model, or Back.\n');
|
|
909
|
-
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
683
|
+
await persistLlmConfig(args.projectDir, { backend: 'anthropic', credentialRef: credential.ref }, validation.models);
|
|
684
|
+
io.stdout.write(`│ LLM ready: yes (${validation.models.default})\n`);
|
|
685
|
+
return { status: 'ready', projectDir: args.projectDir };
|
|
910
686
|
}
|
|
911
687
|
}
|