feat(embed,import): refuse cleanly when --no-embedding deferred-setup is active

T7 of the v0.37.10.0 wave. Both runEmbedCore and runImport now call
assertEmbeddingEnabled(loadConfig()) at entry. When the brain was init'd
with --no-embedding (config has embedding_disabled:true), they exit 1
with a paste-ready hint:

  gbrain config set embedding_model <provider>:<model>
  gbrain config set embedding_dimensions <N>
  gbrain init --force --embedding-model <provider>:<model>

\`gbrain import --no-embed\` flag still works (chunks land without vectors),
so users can still ingest in deferred-setup mode and backfill embeddings
later with \`gbrain embed --stale\`.
This commit is contained in:
Garry Tan
2026-05-21 11:20:15 -07:00
parent ecc20eacbb
commit 8442c37777
2 changed files with 24 additions and 0 deletions
+8
View File
@@ -4,6 +4,8 @@ import type { ChunkInput } from '../core/types.ts';
import { chunkText } from '../core/chunkers/recursive.ts';
import { createProgress, type ProgressReporter } from '../core/progress.ts';
import { getCliOptions, cliOptsToProgressOptions } from '../core/cli-options.ts';
import { assertEmbeddingEnabled } from '../core/embedding-dim-check.ts';
import { loadConfig } from '../core/config.ts';
export interface EmbedOpts {
/** Embed ALL pages (every chunk). */
@@ -67,6 +69,12 @@ export interface EmbedResult {
* auto-embed step) can report embeddings in their own structured output.
*/
export async function runEmbedCore(engine: BrainEngine, opts: EmbedOpts): Promise<EmbedResult> {
// T7 (D9): refuse cleanly when init persisted the deferred-setup sentinel.
// Skipped in dryRun mode so plan-mode introspection still works.
if (!opts.dryRun) {
assertEmbeddingEnabled(loadConfig());
}
const result: EmbedResult = {
embedded: 0,
skipped: 0,
+16
View File
@@ -49,6 +49,22 @@ export async function runImport(
const noEmbed = args.includes('--no-embed');
const fresh = args.includes('--fresh');
const jsonOutput = args.includes('--json');
// T7 (D9): refuse cleanly when init persisted the deferred-setup sentinel,
// unless the user is explicitly skipping embedding via `--no-embed` (in
// which case the chunks land without vectors and the user can backfill
// later with `gbrain embed --stale` after configuring a provider).
if (!noEmbed) {
const { assertEmbeddingEnabled } = await import('../core/embedding-dim-check.ts');
const { loadConfig } = await import('../core/config.ts');
try {
assertEmbeddingEnabled(loadConfig());
} catch (e) {
console.error(`\n${e instanceof Error ? e.message : e}`);
console.error('Tip: run `gbrain import <dir> --no-embed` to import without embedding now.');
process.exit(1);
}
}
// v0.30.x follow-up to PR #707: programmatic sourceId support so internal
// callers (performFullSync, future Step 6 paths) can route to a named
// source. The CLI `gbrain import` deliberately has no --source flag per