Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions hindsight-all/hindsight/embedded.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class HindsightEmbedded:
Args:
profile: Profile name for data isolation (default: "default")
llm_provider: LLM provider ("groq", "openai", "ollama", "gemini", "anthropic", "lmstudio")
llm_api_key: API key for the LLM provider
llm_api_key: API key for the LLM provider. Omit to inherit parent env / profile .env.
llm_model: Model name to use
llm_base_url: Optional custom base URL for LLM API
database_url: Optional database URL override (default: profile-specific pg0)
Expand All @@ -82,7 +82,7 @@ def __init__(
self,
profile: str = "default",
llm_provider: str = "groq",
llm_api_key: str = "",
llm_api_key: Optional[str] = None,
llm_model: str = "openai/gpt-oss-120b",
llm_base_url: Optional[str] = None,
database_url: Optional[str] = None,
Expand All @@ -98,7 +98,8 @@ def __init__(
Args:
profile: Profile name for data isolation
llm_provider: LLM provider
llm_api_key: API key for the LLM provider
llm_api_key: API key for the LLM provider. Omit to inherit parent env / profile .env;
pass "" to explicitly use no key.
llm_model: Model name to use
llm_base_url: Optional custom base URL for LLM API
database_url: Optional database URL override
Expand All @@ -110,15 +111,18 @@ def __init__(
"""
self.profile = profile

# Build config dict for daemon (matches CLI format)
# Omit an unspecified key so the daemon can inherit it from the profile
# or parent environment (#3253). An explicit empty string remains an override.
self.config = {
"HINDSIGHT_API_LLM_PROVIDER": llm_provider,
"HINDSIGHT_API_LLM_API_KEY": llm_api_key,
"HINDSIGHT_API_LLM_MODEL": llm_model,
"HINDSIGHT_API_LOG_LEVEL": log_level,
"HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT": str(idle_timeout),
}

if llm_api_key is not None:
self.config["HINDSIGHT_API_LLM_API_KEY"] = llm_api_key

if llm_base_url:
self.config["HINDSIGHT_API_LLM_BASE_URL"] = llm_base_url

Expand Down
20 changes: 20 additions & 0 deletions hindsight-all/tests/test_embedded_namespaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ def embedded_client():
)


def test_llm_api_key_three_state_config():
"""llm_api_key uses three-state semantics (#3253).

None/omitted → key absent (inherit parent env / profile .env)
"" → present as empty string (explicit clear)
non-empty → present as override
"""
omitted = HindsightEmbedded(profile="test", llm_provider="openai")
assert "HINDSIGHT_API_LLM_API_KEY" not in omitted.config
assert omitted.config["HINDSIGHT_API_LLM_PROVIDER"] == "openai"

empty = HindsightEmbedded(profile="test", llm_provider="openai", llm_api_key="")
assert empty.config["HINDSIGHT_API_LLM_API_KEY"] == ""

provided = HindsightEmbedded(
profile="test", llm_provider="openai", llm_api_key="sk-real"
)
assert provided.config["HINDSIGHT_API_LLM_API_KEY"] == "sk-real"


def test_banks_create_ensures_daemon_started(embedded_client):
"""Test that banks.create() calls _ensure_started()."""
# Mock _ensure_started to track calls
Expand Down