Skip to content
Closed
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
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,15 @@ for all 9 per-language benchmark deep dives.

All configuration is via environment variables:

| Variable | Default | Description |
| ------------------------ | ------------------------ | ------------------------------------------ |
| `LUMEN_EMBED_MODEL` | see note ¹ | Embedding model (must be in registry) |
| `LUMEN_BACKEND` | `ollama` | Embedding backend (`ollama` or `lmstudio`) |
| `OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
| `LM_STUDIO_HOST` | `http://localhost:1234` | LM Studio server URL |
| `LUMEN_MAX_CHUNK_TOKENS` | `512` | Max tokens per chunk before splitting |
| Variable | Default | Description |
| ------------------------ | ------------------------ | ------------------------------------------------------------- |
| `LUMEN_EMBED_MODEL` | see note ¹ | Embedding model; use with `LUMEN_EMBED_DIMS` for unlisted models |
| `LUMEN_BACKEND` | `ollama` | Embedding backend (`ollama` or `lmstudio`) |
| `OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
| `LM_STUDIO_HOST` | `http://localhost:1234` | LM Studio server URL |
| `LUMEN_MAX_CHUNK_TOKENS` | `512` | Max tokens per chunk before splitting |
| `LUMEN_EMBED_DIMS` | — | Override embedding dimensions (required for unlisted models) |
| `LUMEN_EMBED_CTX` | `8192` (unlisted models) | Override context window length |

¹ `ordis/jina-embeddings-v2-base-code` (Ollama),
`nomic-ai/nomic-embed-code-GGUF` (LM Studio)
Expand All @@ -210,6 +212,22 @@ Dimensions and context length are configured automatically per model:
Switching models creates a separate index automatically. The model name is part
of the database path hash, so different models never collide.

### Using a custom or unlisted model

If your model is not in the registry above, set `LUMEN_EMBED_DIMS` to bypass the
registry check. `LUMEN_EMBED_CTX` is optional and defaults to `8192`.

Both variables can also override values for _known_ models — useful when running
a model variant with a longer context window or different output dimensions.

```sh
LUMEN_BACKEND=lmstudio
LM_STUDIO_HOST=http://localhost:8801
LUMEN_EMBED_MODEL=mlx-community/Qwen3-Embedding-8B-4bit-DWQ
LUMEN_EMBED_DIMS=4096
LUMEN_EMBED_CTX=40960 # optional, defaults to 8192
```

## Controlling what gets indexed

Lumen filters files through six layers: built-in directory and lock file skips →
Expand Down
32 changes: 19 additions & 13 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,27 @@ func Load() (Config, error) {

model := EnvOrDefault("LUMEN_EMBED_MODEL", defaultModel)

// Allow fully custom models via LUMEN_EMBED_DIMS + LUMEN_EMBED_CTX,
// bypassing the KnownModels registry. Useful for any OpenAI-compat server.
var dims, ctxLength int
if d := EnvOrDefaultInt("LUMEN_EMBED_DIMS", 0); d > 0 {
dims = d
ctxLength = EnvOrDefaultInt("LUMEN_EMBED_CTX", 8192)
} else {
spec, ok := embedder.KnownModels[model]
if !ok {
return Config{}, fmt.Errorf("unknown embedding model %q: set LUMEN_EMBED_DIMS to use an unlisted model", model)
}
dims = spec.Dims
ctxLength = spec.CtxLength
overrideDims := EnvOrDefaultInt("LUMEN_EMBED_DIMS", 0)
overrideCtx := EnvOrDefaultInt("LUMEN_EMBED_CTX", 0)

spec, modelKnown := embedder.KnownModels[model]
if !modelKnown && overrideDims == 0 {
return Config{}, fmt.Errorf("unknown embedding model %q: set LUMEN_EMBED_DIMS to use an unlisted model", model)
}

dims := spec.Dims
ctxLength := spec.CtxLength

if overrideDims > 0 {
dims = overrideDims
}
if overrideCtx > 0 {
ctxLength = overrideCtx
} else if !modelKnown {
ctxLength = 8192
}


return Config{
Model: model,
Dims: dims,
Expand Down
96 changes: 96 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,102 @@ func TestEnvOrDefaultInt(t *testing.T) {
}
}

func TestLoad(t *testing.T) {
tests := []struct {
name string
model string
dims string
ctx string
wantDims int
wantCtx int
wantErr bool
}{
{
name: "known model no overrides",
model: "ordis/jina-embeddings-v2-base-code",
wantDims: 768,
wantCtx: 8192,
},
{
name: "unknown model with DIMS only",
model: "custom-model",
dims: "4096",
wantDims: 4096,
wantCtx: 8192,
},
{
name: "unknown model with DIMS and CTX",
model: "custom-model",
dims: "4096",
ctx: "40960",
wantDims: 4096,
wantCtx: 40960,
},
{
name: "unknown model no DIMS",
model: "custom-model",
wantErr: true,
},
{
name: "unknown model CTX only no DIMS",
model: "custom-model",
ctx: "8192",
wantErr: true,
},
{
name: "known model DIMS override",
model: "ordis/jina-embeddings-v2-base-code",
dims: "512",
wantDims: 512,
wantCtx: 8192,
},
{
name: "known model CTX override",
model: "ordis/jina-embeddings-v2-base-code",
ctx: "16384",
wantDims: 768,
wantCtx: 16384,
},
{
name: "known model both overrides",
model: "ordis/jina-embeddings-v2-base-code",
dims: "512",
ctx: "4096",
wantDims: 512,
wantCtx: 4096,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Setenv("LUMEN_EMBED_MODEL", tc.model)
if tc.dims != "" {
t.Setenv("LUMEN_EMBED_DIMS", tc.dims)
}
if tc.ctx != "" {
t.Setenv("LUMEN_EMBED_CTX", tc.ctx)
}

cfg, err := Load()
if tc.wantErr {
if err == nil {
t.Fatal("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.Dims != tc.wantDims {
t.Errorf("Dims: got %d, want %d", cfg.Dims, tc.wantDims)
}
if cfg.CtxLength != tc.wantCtx {
t.Errorf("CtxLength: got %d, want %d", cfg.CtxLength, tc.wantCtx)
}
})
}
}

func TestDBPathForProject(t *testing.T) {
t.Run("deterministic", func(t *testing.T) {
p1 := DBPathForProject("/home/user/project", "model-a")
Expand Down
Loading