Skip to content
Merged
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
10 changes: 8 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ project indexed and the MCP endpoint answering queries.
## Install

```bash
go install github.com/RandomCodeSpace/docsiq@latest
GOFLAGS='-tags=sqlite_fts5' go install github.com/RandomCodeSpace/docsiq@latest
```

The `sqlite_fts5` build tag is required — docsiq uses SQLite's FTS5
full-text-search extension. Without it the binary fails at runtime with
`no such module: fts5` when opening a project store. `go install` does
**not** pick up Makefile tags, so the flag has to come from `GOFLAGS`
(or `-tags`) on the install command itself.

This drops a `docsiq` binary into `$(go env GOPATH)/bin`. Make sure
that path is on your `PATH`.

Expand All @@ -35,7 +41,7 @@ Alternatively, build from a checkout:
```bash
git clone https://github.com/RandomCodeSpace/docsiq.git
cd docsiq
CGO_ENABLED=1 go build -o docsiq .
CGO_ENABLED=1 go build -tags sqlite_fts5 -o docsiq .
```

## First project
Expand Down
11 changes: 11 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ func Load(cfgFile string) (*Config, error) {
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AutomaticEnv()

// Bind every defaulted key to its DOCSIQ_<UPPER_KEY> env variant so
// v.Unmarshal honours overrides without requiring a config file.
// Without this, only keys explicitly listed via BindEnv are read
// from env when nothing in the config tree already mentions them
// (Viper #761). The explicit BindEnv calls below stay because they
// document the secondary DOCSIQ_API_KEY alias for server.api_key
// and harmlessly double-bind the rest.
for _, key := range v.AllKeys() {
_ = v.BindEnv(key)
}

// Explicit alias: DOCSIQ_API_KEY is a convenience shortcut for
// DOCSIQ_SERVER_API_KEY (the auth-middleware API key). Bind both so
// either form populates server.api_key. BindEnv names are matched
Expand Down
42 changes: 42 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -719,3 +719,45 @@ func TestLoad_LogFormatFromEnv(t *testing.T) {
t.Errorf("env Log.Format=%q want json", cfg.Log.Format)
}
}

// TestLoad_EnvOverridesLLM is the regression test for the bug where
// DOCSIQ_LLM_* env vars were silently ignored without a config file
// because only a handful of keys were explicitly bound via BindEnv
// (Viper #761). After the fix, every defaulted key — including the
// nested LLM provider keys — must be reachable via env.
func TestLoad_EnvOverridesLLM(t *testing.T) {
home := t.TempDir()
isolateEnv(t, home)

t.Setenv("DOCSIQ_LLM_OLLAMA_CHAT_MODEL", "envtest-chat")
t.Setenv("DOCSIQ_LLM_OLLAMA_EMBED_MODEL", "envtest-embed")
t.Setenv("DOCSIQ_LLM_PROVIDER", "azure")
t.Setenv("DOCSIQ_LLM_AZURE_API_KEY", "envtest-azure-key")
t.Setenv("DOCSIQ_LLM_AZURE_ENDPOINT", "https://envtest.openai.azure.com/")
t.Setenv("DOCSIQ_LLM_AZURE_CHAT_MODEL", "envtest-azure-chat")
t.Setenv("DOCSIQ_LLM_AZURE_EMBED_MODEL", "envtest-azure-embed")
t.Setenv("DOCSIQ_DATA_DIR", filepath.Join(home, "envtest-data"))

cfg, err := Load("")
if err != nil {
t.Fatalf("Load: %v", err)
}

// Ollama keys must round-trip even when provider is azure — the
// fix is about *reachability* of the keys, not which one is active.
if got, want := cfg.LLM.Ollama.ChatModel, "envtest-chat"; got != want {
t.Errorf("LLM.Ollama.ChatModel = %q, want %q", got, want)
}
if got, want := cfg.LLM.Ollama.EmbedModel, "envtest-embed"; got != want {
t.Errorf("LLM.Ollama.EmbedModel = %q, want %q", got, want)
}
if got, want := cfg.LLM.Provider, "azure"; got != want {
t.Errorf("LLM.Provider = %q, want %q", got, want)
}
if got, want := cfg.LLM.Azure.APIKey, "envtest-azure-key"; got != want {
t.Errorf("LLM.Azure.APIKey = %q, want %q", got, want)
}
if got, want := cfg.DataDir, filepath.Join(home, "envtest-data"); got != want {
t.Errorf("DataDir = %q, want %q", got, want)
}
}
Loading