Skip to content

fix: respect openai_api_key from ~/.gbrain/config.json in embed/search paths#482

Closed
jacopone wants to merge 1 commit into
garrytan:masterfrom
jacopone:fix/embedding-config-fallback
Closed

fix: respect openai_api_key from ~/.gbrain/config.json in embed/search paths#482
jacopone wants to merge 1 commit into
garrytan:masterfrom
jacopone:fix/embedding-config-fallback

Conversation

@jacopone

Copy link
Copy Markdown

Summary

gbrain config set openai_api_key X writes the key to the engine's config table via engine.setConfig, but embedding.ts:24 instantiates new OpenAI() with no args — which only reads process.env.OPENAI_API_KEY. Same gap exists in operations.ts:278 (auto-embed-on-write gate) and search/hybrid.ts:81 (vector search gate).

Net effect: users who run gbrain config set openai_api_key sk-... (or write the key to ~/.gbrain/config.json directly) hit "OPENAI_API_KEY environment variable is missing or empty" errors despite the key being persisted in two places gbrain reasonably reads from. The CLI says success, the embed says missing.

This is a real UX issue on systems that deliberately don't shell-export API keys — NixOS + home-manager, per-process .env loading via python-dotenv, anywhere least-privilege shell hygiene is the convention.

Repro

unset OPENAI_API_KEY
gbrain init --pglite
gbrain config set openai_api_key sk-...
gbrain config get openai_api_key  # ✅ returns the key
echo "test" | gbrain put test-page  # writes ok, but auto-embed silently skipped (operations.ts:278)
gbrain embed --stale  # ❌ fails: "OPENAI_API_KEY environment variable is missing or empty"
gbrain query "test"  # ❌ falls back to keyword-only (hybrid.ts:81)

Fix

Adds getOpenAIKey() helper to core/config.ts that returns env-first, then falls back to reading openai_api_key from ~/.gbrain/config.json directly (independent of engine config — works even when no DB is configured). Three call sites updated:

File Change
core/embedding.ts new OpenAI({ apiKey: getOpenAIKey() })
core/operations.ts:278 auto-embed gate respects file fallback
core/search/hybrid.ts:81 vector search gate respects file fallback

Same env > file precedence that loadConfig() already uses for database_url.

Verification

$ unset OPENAI_API_KEY
$ # Key is in ~/.gbrain/config.json (mode 0600), nothing in shell env
$ echo "test" | gbrain put test-page  # ✅ auto-embeds (was: silently skipped)
$ gbrain embed --stale                # ✅ works (was: failed)
$ gbrain query "test"                 # ✅ semantic results (was: keyword-only)

Tests

bun test → 2164 pass / 0 fail / 250 skip (skips are pre-existing env-conditional tests, unrelated). Patch is +27 / -4 lines.

2164 pass
 250 skip
   0 fail
5599 expect() calls
Ran 2414 tests across 128 files. [74.03s]

Out of scope (possible follow-ups)

  • The PGLite-stored copy from gbrain config set openai_api_key X is now dead code. A future PR could route bootstrap-key writes (engine, database_url, *_api_key, *_token) to the file instead of the engine table.
  • Adding a gbrain config unset <key> command would help users clean up the dead PGLite row left behind by old setups.

Test plan

  • bun test passes
  • Manual verification with unset OPENAI_API_KEY and key in ~/.gbrain/config.json only
  • gbrain put auto-embeds via the patched gate
  • gbrain query returns semantic (not keyword-only) results
  • gbrain embed --stale runs without env

🤖 Generated with Claude Code

…h paths

`gbrain config set openai_api_key X` writes to engine.setConfig (PGLite/PG)
but embedding.ts:24 instantiates `new OpenAI()` with no args, which only
reads process.env.OPENAI_API_KEY. Same gap in operations.ts:278 (auto-embed
gate) and search/hybrid.ts:81 (vector search gate). Users who set the key
via `gbrain config set` or by editing ~/.gbrain/config.json directly hit
"OPENAI_API_KEY missing" errors despite the key being persisted.

Real UX issue on systems that deliberately keep API keys out of interactive
shell env (NixOS+home-manager, per-process .env loading via python-dotenv,
anywhere least-privilege shell hygiene is the convention).

Adds `getOpenAIKey()` helper to core/config.ts: env first, then reads
openai_api_key from the config file directly. Independent of engine config —
works even when no DB is configured. Three call sites updated to use it.

Same env > file precedence pattern that loadConfig() already uses for
database_url. PGLite-stored copy from `config set` becomes dead code; a
follow-up could route bootstrap-key writes to the file instead.

Tests: bun test → 2164 pass, 0 fail, 250 skip (unrelated env-conditional).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@garrytan

garrytan commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Thanks for this contribution — and apologies for the slow triage. We did a full pass over the entire PR backlog. gbrain has moved fast, and the maintainer's larger "cathedral" rewrites have superseded a big share of community PRs: the AI gateway + recipes + user_provided_models system replaced almost all individual provider PRs; #1805 fixed the whole Postgres module-singleton class; #1542 unified the type taxonomy; #1657 the retrieval path; #1802 the doctor; and so on.

We're closing this one in that cleanup — either the fix already landed on master, it duplicates another PR or merged change, or it's outside the current merge bar. Where a closed PR carried a genuinely valuable idea, we've recorded it in docs/designs/COMMUNITY_IDEAS.md so nothing good is lost (a few may graduate into TODOs).

Please don't read the close as a judgment of the work — thank you for contributing. If you believe the underlying issue is still live on the latest master, reopen with a quick note and we'll take another look. 🙏

@garrytan garrytan closed this Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants