# `Mnemosyne.Config`
[🔗](https://github.com/edlontech/mnemosyne/blob/main/lib/mnemosyne/config.ex#L1)

Unified configuration for Mnemosyne LLM and embedding settings.

Holds default model configuration for LLM and embedding calls,
plus per-step overrides that merge on top of defaults. Configuration
is validated at load time using Zoi schemas to catch misconfiguration early.

## Fields

* `:session` (`t:map/0`) - Required. Session auto-commit and timeout configuration The default value is `%{auto_commit: true, flush_timeout_ms: 120000, session_timeout_ms: 600000}`.

* `:overrides` (`t:map/0`) - Required. Per-step LLM overrides keyed by pipeline step atom The default value is `%{}`.

* `:embedding` (`t:map/0`) - Required. Default embedding model configuration for vector generation

* `:backend` (`t:map/0`) - Graph backend configuration

* `:llm` (`t:map/0`) - Required. Default LLM configuration applied to all pipeline steps

* `:value_function` (`t:map/0`) - Required. Value function module and per-node-type scoring parameters The default value is `%{module: Mnemosyne.ValueFunction.Default, params: %{tag: %{k: 5, threshold: 0.9, lambda: 0.01, top_k: 10, base_floor: 0.3, beta: 1.0}, source: %{k: 5, threshold: 0.0, lambda: 0.01, top_k: 50, base_floor: 0.3, beta: 1.0}, semantic: %{k: 5, threshold: 0.0, lambda: 0.01, top_k: 20, base_floor: 0.3, beta: 1.0}, procedural: %{k: 5, threshold: 0.8, lambda: 0.01, top_k: 10, base_floor: 0.3, beta: 1.0}, episodic: %{k: 5, threshold: 0.0, lambda: 0.01, top_k: 30, base_floor: 0.3, beta: 1.0}, subgoal: %{k: 5, threshold: 0.75, lambda: 0.01, top_k: 10, base_floor: 0.3, beta: 1.0}, intent: %{k: 5, threshold: 0.7, lambda: 0.01, top_k: 10, base_floor: 0.3, beta: 1.0}}}`.

* `:episodic_validation` (`t:map/0`) - Required.  The default value is `nil`.

* `:intent_merge_threshold` (`t:float/0`) - Required.  The default value is `0.8`.

* `:intent_identity_threshold` (`t:float/0`) - Required.  The default value is `0.95`.

* `:refinement_threshold` (`t:float/0`) - Required.  The default value is `0.6`.

* `:refinement_budget` (`t:integer/0`) - Required.  The default value is `1`.

* `:plateau_delta` (`t:float/0`) - Required.  The default value is `0.05`.

* `:trace_verbosity` (`:summary` | `:detailed`) - Required.  The default value is `:summary`.

* `:extraction_profile` (`t:term/0`) - Required.  The default value is `nil`.

## Override Resolution

When a pipeline step (e.g. `:structuring`, `:retrieval`) has an entry in
`:overrides`, the override's `:model` replaces the default and its `:opts`
are deep-merged with the base LLM opts. This lets you use a cheaper model
for simple extraction steps while keeping a powerful model for reasoning.

## Examples

Minimal configuration with defaults:

    config = %Mnemosyne.Config{
      llm: %{model: "gpt-4o", opts: %{}},
      embedding: %{model: "text-embedding-3-small", opts: %{}}
    }

With per-step overrides:

    config = %Mnemosyne.Config{
      llm: %{model: "gpt-4o", opts: %{temperature: 0.7}},
      embedding: %{model: "text-embedding-3-small", opts: %{}},
      overrides: %{
        structuring: %{model: "gpt-4o-mini"},
        retrieval: %{opts: %{temperature: 0.0}}
      }
    }

Loading from application environment:

    # In config/config.exs
    config :mnemosyne, :config,
      llm: %{model: "gpt-4o", opts: %{temperature: 0.7}},
      embedding: %{model: "text-embedding-3-small", opts: %{}}

    # At runtime
    {:ok, config} = Mnemosyne.Config.from_env()

# `t`

```elixir
@type t() :: %Mnemosyne.Config{
  backend: nil | %{module: atom(), opts: map()},
  embedding: %{opts: map(), model: binary()},
  episodic_validation: nil | map(),
  extraction_profile: nil | any(),
  intent_identity_threshold: float(),
  intent_merge_threshold: float(),
  llm: %{opts: map(), model: binary()},
  overrides: %{
    optional(atom()) =&gt; %{
      optional(:opts) =&gt; map(),
      optional(:model) =&gt; binary()
    }
  },
  plateau_delta: float(),
  refinement_budget: integer(),
  refinement_threshold: float(),
  session: %{
    auto_commit: boolean(),
    flush_timeout_ms: integer() | :infinity,
    session_timeout_ms: integer() | :infinity
  },
  trace_verbosity: :summary | :detailed,
  value_function: %{
    module: atom(),
    params: %{
      optional(atom()) =&gt; %{
        k: integer(),
        threshold: float(),
        lambda: float(),
        top_k: integer(),
        base_floor: float(),
        beta: float()
      }
    }
  }
}
```

# `embedding_opts`

```elixir
@spec embedding_opts(t() | nil) :: keyword()
```

Returns embedding keyword opts from the config as a flat keyword list.

Returns an empty list when config is `nil`, allowing callers to work
without configuration.

# `from_env`

```elixir
@spec from_env() :: {:ok, t()} | {:error, Mnemosyne.Errors.Invalid.ConfigError.t()}
```

Loads and validates config from the `:mnemosyne` application environment.

Reads the `:config` key under the `:mnemosyne` application and validates it
against the Zoi schema. Returns `{:error, ConfigError}` when the key is
missing or the data fails validation.

## Examples

    # When configured:
    {:ok, %Mnemosyne.Config{}} = Mnemosyne.Config.from_env()

    # When missing:
    {:error, %Mnemosyne.Errors.Invalid.ConfigError{reason: :no_config}} = Mnemosyne.Config.from_env()

# `llm_opts`

```elixir
@spec llm_opts(t() | nil, atom(), keyword()) :: keyword()
```

Returns LLM keyword opts for a pipeline step, merging overrides with base opts.

Resolves the model and opts for the given `step`, then flattens the result
into a keyword list suitable for passing directly to an LLM adapter call.
The `base_opts` (typically pipeline-specific options like prompt messages)
are appended after the resolved config options.

Returns `base_opts` unchanged when config is `nil`, allowing callers to
work without configuration.

# `resolve`

```elixir
@spec resolve(t(), atom()) :: %{model: String.t(), opts: map()}
```

Returns the LLM model and opts for the given pipeline step, applying any overrides.

Looks up the `step` atom in `config.overrides`. If an override exists, its
`:model` replaces the base model (when present) and its `:opts` are merged
on top of the base opts. When no override exists, the base LLM config is
returned as-is.

## Examples

    iex> config = %Mnemosyne.Config{
    ...>   llm: %{model: "gpt-4o", opts: %{temperature: 0.7}},
    ...>   embedding: %{model: "e5-base-v2", opts: %{}},
    ...>   overrides: %{structuring: %{model: "gpt-4o-mini"}}
    ...> }
    iex> Mnemosyne.Config.resolve(config, :structuring)
    %{model: "gpt-4o-mini", opts: %{temperature: 0.7}}
    iex> Mnemosyne.Config.resolve(config, :retrieval)
    %{model: "gpt-4o", opts: %{temperature: 0.7}}

# `resolve_embedding`

```elixir
@spec resolve_embedding(t()) :: %{model: String.t(), opts: map()}
```

Returns the embedding model and opts from the config.

Unlike `resolve/2`, embeddings have no per-step overrides since the same
embedding model must be used consistently across the entire knowledge graph
to keep vector spaces comparable.

# `resolve_overlay`

```elixir
@spec resolve_overlay(t() | nil, atom()) :: String.t() | nil
```

Returns the overlay text for a given pipeline step, or nil if no profile or overlay exists.

# `resolve_value_function`

```elixir
@spec resolve_value_function(t(), atom()) :: map()
```

Returns the value function params for a given node type.

Looks up the type in `config.value_function.params` and merges with
per-type defaults. Returns safe defaults for unknown types.

# `t`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
