From a2dcd4eb08027ef9dd7640bd6561068d75ae61bc Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Tue, 12 May 2026 10:26:07 +0200 Subject: [PATCH] fix: guide dev ingest llm setup (#15) Co-authored-by: Andrey Avtomonov <7889985+andreybavt@users.noreply.github.com> --- packages/cli/src/ingest.test.ts | 70 +++++++++++++++++++ .../src/ingest/local-bundle-runtime.test.ts | 8 ++- .../src/ingest/local-bundle-runtime.ts | 12 +++- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/ingest.test.ts b/packages/cli/src/ingest.test.ts index 3b580cc1..59df5e86 100644 --- a/packages/cli/src/ingest.test.ts +++ b/packages/cli/src/ingest.test.ts @@ -32,6 +32,7 @@ import { writeWarehouseConfig, } from './ingest.test-utils.js'; import { resetVizFallbackWarningsForTest } from './viz-fallback.js'; +import { runKtxSetup } from './setup.js'; describe('runKtxIngest', () => { let tempDir: string; @@ -105,6 +106,75 @@ describe('runKtxIngest', () => { expect(statusIo.stderr()).toBe(''); }); + it('prints provider setup guidance when a skip-llm setup project runs dev ingest', async () => { + const projectDir = join(tempDir, 'project'); + const setupIo = makeIo(); + await expect( + runKtxSetup( + { + command: 'run', + projectDir, + mode: 'new', + agents: false, + agentScope: 'project', + agentInstallMode: 'cli', + skipAgents: true, + inputMode: 'disabled', + yes: true, + cliVersion: '0.0.0-test', + skipLlm: true, + skipEmbeddings: true, + databaseDrivers: ['postgres'], + databaseConnectionId: 'warehouse', + databaseUrl: 'env:WAREHOUSE_URL', + databaseSchemas: [], + enableHistoricSql: true, + skipDatabases: false, + skipSources: true, + }, + setupIo.io, + { + databasesDeps: { + testConnection: async (_projectDir, _connectionId, io) => { + io.stdout.write('Driver: postgres\nTables: 1\n'); + return 0; + }, + scanConnection: async () => 0, + historicSqlProbe: async () => ({ ok: true, lines: ['PASS Historic SQL probe skipped in test'] }), + }, + context: async () => ({ status: 'skipped', projectDir }), + }, + ), + ).resolves.toBe(0); + + const sourceDir = join(tempDir, 'source'); + await mkdir(join(sourceDir, 'orders'), { recursive: true }); + await writeFile(join(sourceDir, 'orders', 'orders.json'), '{"name":"orders"}\n', 'utf-8'); + + const runIo = makeIo(); + await expect( + runKtxIngest( + { + command: 'run', + projectDir, + connectionId: 'warehouse', + adapter: 'historic-sql', + sourceDir, + outputMode: 'plain', + }, + runIo.io, + ), + ).resolves.toBe(1); + + expect(runIo.stdout()).toBe(''); + expect(runIo.stderr()).toContain( + 'ktx dev ingest run requires llm.provider.backend: anthropic, vertex, or gateway, or an injected agentRunner.', + ); + expect(runIo.stderr()).toContain( + `ktx setup --project-dir ${projectDir} --anthropic-api-key-env ANTHROPIC_API_KEY --anthropic-model claude-sonnet-4-6 --no-input`, + ); + }); + it('routes metabase scheduled pulls to the fan-out runner and prints child summaries', async () => { const projectDir = join(tempDir, 'project'); await writeMetabaseConfig(projectDir); diff --git a/packages/context/src/ingest/local-bundle-runtime.test.ts b/packages/context/src/ingest/local-bundle-runtime.test.ts index 779c2cc3..d8cd3907 100644 --- a/packages/context/src/ingest/local-bundle-runtime.test.ts +++ b/packages/context/src/ingest/local-bundle-runtime.test.ts @@ -53,7 +53,13 @@ describe('createLocalBundleIngestRuntime', () => { project, adapters: [new FakeSourceAdapter()], }), - ).toThrow('ktx dev ingest run requires llm.provider.backend: anthropic, vertex, or gateway, or an injected agentRunner'); + ).toThrow( + [ + 'ktx dev ingest run requires llm.provider.backend: anthropic, vertex, or gateway, or an injected agentRunner.', + `Configure an Anthropic provider, then rerun ingest:`, + ` ktx setup --project-dir ${project.projectDir} --anthropic-api-key-env ANTHROPIC_API_KEY --anthropic-model claude-sonnet-4-6 --no-input`, + ].join('\n'), + ); }); it('builds runner deps with local SQLite stores and context tools enabled', async () => { diff --git a/packages/context/src/ingest/local-bundle-runtime.ts b/packages/context/src/ingest/local-bundle-runtime.ts index 43d0247b..afcb2525 100644 --- a/packages/context/src/ingest/local-bundle-runtime.ts +++ b/packages/context/src/ingest/local-bundle-runtime.ts @@ -536,6 +536,14 @@ function nextLocalJobId(): string { return `local-${Date.now().toString(36)}`; } +function localIngestLlmProviderGuardMessage(projectDir: string): string { + return [ + 'ktx dev ingest run requires llm.provider.backend: anthropic, vertex, or gateway, or an injected agentRunner.', + 'Configure an Anthropic provider, then rerun ingest:', + ` ktx setup --project-dir ${projectDir} --anthropic-api-key-env ANTHROPIC_API_KEY --anthropic-model claude-sonnet-4-6 --no-input`, + ].join('\n'); +} + function resolveAgentRunner(options: CreateLocalBundleIngestRuntimeOptions): { agentRunner: AgentRunnerService; llmProvider?: KtxLlmProvider; @@ -548,9 +556,7 @@ function resolveAgentRunner(options: CreateLocalBundleIngestRuntimeOptions): { } if (!llmProvider) { - throw new Error( - 'ktx dev ingest run requires llm.provider.backend: anthropic, vertex, or gateway, or an injected agentRunner', - ); + throw new Error(localIngestLlmProviderGuardMessage(options.project.projectDir)); } return {