LLMDB.Merge (LLM DB v2026.3.0)

Copy Markdown View Source

Precedence-aware merging with exclude handling for LLM model data.

Provides functions to merge providers, models, and arbitrary maps with configurable precedence rules. Handles excludes via exact match or glob patterns.

Summary

Functions

Compiles exclude patterns to regex for performance.

Converts a glob pattern to an anchored regex.

Checks if a model_id matches any exclude pattern.

Merges two maps with precedence rules.

Merges two lists of maps by a shared ID key.

Merges two model lists by {provider, id} identity, applying excludes.

Merges two provider lists by :id key.

Creates a configurable deep merge resolver function.

Functions

compile_excludes(excludes)

@spec compile_excludes(map()) :: map()

Compiles exclude patterns to regex for performance.

Converts a map of %{provider => [patterns]} to %{provider => [compiled_patterns]} where each pattern is either kept as a string (for exact match) or compiled to regex (for globs).

Examples

iex> result = LLMDB.Merge.compile_excludes(%{openai: ["gpt-3", "gpt-5-*"]})
iex> [exact, pattern] = result.openai
iex> exact
"gpt-3"
iex> Regex.match?(pattern, "gpt-5-pro")
true

compile_pattern(pattern)

@spec compile_pattern(String.t()) :: Regex.t()

Converts a glob pattern to an anchored regex.

  • "" becomes "."
  • Escape other regex special chars
  • Anchor with ^ and $

Examples

iex> pattern = LLMDB.Merge.compile_pattern("gpt-*")
iex> Regex.match?(pattern, "gpt-4")
true

iex> pattern = LLMDB.Merge.compile_pattern("gpt-5-*-mini")
iex> Regex.match?(pattern, "gpt-5-turbo-mini")
true

matches_exclude?(model_id, patterns)

@spec matches_exclude?(String.t() | nil, [String.t() | Regex.t()]) :: boolean()

Checks if a model_id matches any exclude pattern.

Patterns can be exact strings or compiled regexes.

Examples

iex> LLMDB.Merge.matches_exclude?("gpt-4", ["gpt-3", "gpt-5"])
false

iex> LLMDB.Merge.matches_exclude?("gpt-3", ["gpt-3", "gpt-5"])
true

iex> LLMDB.Merge.matches_exclude?("gpt-5-pro", [~r/^gpt-5-.*$/])
true

iex> LLMDB.Merge.matches_exclude?("gpt-4", [~r/^gpt-5-.*$/])
false

merge(base, override, precedence)

@spec merge(map(), map(), :higher | :lower) :: map()

Merges two maps with precedence rules.

  • Scalar values: higher precedence wins
  • Maps: deep merge recursively
  • Lists: concat and de-dup by value
  • Higher precedence source always wins on scalars

Examples

iex> LLMDB.Merge.merge(%{a: 1}, %{b: 2}, :higher)
%{a: 1, b: 2}

iex> LLMDB.Merge.merge(%{a: 1}, %{a: 2}, :higher)
%{a: 2}

iex> LLMDB.Merge.merge(%{a: 1}, %{a: 2}, :lower)
%{a: 1}

iex> LLMDB.Merge.merge(%{a: %{b: 1}}, %{a: %{c: 2}}, :higher)
%{a: %{b: 1, c: 2}}

iex> LLMDB.Merge.merge(%{a: [1, 2]}, %{a: [2, 3]}, :higher)
%{a: [1, 2, 3]}

merge_list_by_id(base_list, override_list, id_key \\ :id)

@spec merge_list_by_id([map()], [map()], atom() | String.t()) :: [map()]

Merges two lists of maps by a shared ID key.

Keeps the base list order, overrides items with matching IDs from the override list, and appends override-only items in their original order.

Used by LLMDB.Pricing to merge pricing components from provider defaults with model-specific overrides.

Parameters

  • base_list - The base list of maps (order preserved)
  • override_list - Maps that override or extend the base list
  • id_key - The key to match on (default: :id). Supports both atom and string keys.

Examples

# Override matching items, preserve order
iex> base = [%{id: "a", value: 1}, %{id: "b", value: 2}]
iex> override = [%{id: "b", value: 20}]
iex> LLMDB.Merge.merge_list_by_id(base, override)
[%{id: "a", value: 1}, %{id: "b", value: 20}]

# Append new items from override
iex> base = [%{id: "a", value: 1}]
iex> override = [%{id: "b", value: 2}, %{id: "c", value: 3}]
iex> LLMDB.Merge.merge_list_by_id(base, override)
[%{id: "a", value: 1}, %{id: "b", value: 2}, %{id: "c", value: 3}]

# Pricing component merge example
iex> defaults = [%{id: "tool.search", rate: 10.0}, %{id: "tool.code", rate: 5.0}]
iex> overrides = [%{id: "tool.search", rate: 0.0}]  # Free search
iex> LLMDB.Merge.merge_list_by_id(defaults, overrides)
[%{id: "tool.search", rate: 0.0}, %{id: "tool.code", rate: 5.0}]

merge_models(base_models, override_models, excludes)

@spec merge_models([map()], [map()], map()) :: [map()]

Merges two model lists by {provider, id} identity, applying excludes.

  • Merge models by {provider, id} identity
  • Apply excludes: %{provider_atom => [patterns]} where patterns can be exact strings or globs with *
  • Compile glob patterns to regex once for performance
  • Higher precedence (override) wins on conflicts

Examples

iex> base = [%{id: "gpt-4", provider: :openai}]
iex> override = [%{id: "gpt-4", provider: :openai, capabilities: %{tools: true}}]
iex> LLMDB.Merge.merge_models(base, override, %{})
[%{id: "gpt-4", provider: :openai, capabilities: %{tools: true}}]

iex> base = [%{id: "gpt-4", provider: :openai}, %{id: "gpt-3", provider: :openai}]
iex> excludes = %{openai: ["gpt-3"]}
iex> LLMDB.Merge.merge_models(base, [], excludes)
[%{id: "gpt-4", provider: :openai}]

iex> base = [%{id: "gpt-4o-mini", provider: :openai}, %{id: "gpt-5-pro", provider: :openai}]
iex> excludes = %{openai: ["gpt-5-*"]}
iex> LLMDB.Merge.merge_models(base, [], excludes)
[%{id: "gpt-4o-mini", provider: :openai}]

merge_providers(base_providers, override_providers)

@spec merge_providers([map()], [map()]) :: [map()]

Merges two provider lists by :id key.

Higher precedence (override) wins on conflicts.

Examples

iex> base = [%{id: :openai, name: "OpenAI"}]
iex> override = [%{id: :openai, name: "OpenAI Updated"}, %{id: :anthropic, name: "Anthropic"}]
iex> result = LLMDB.Merge.merge_providers(base, override)
iex> Enum.sort_by(result, & &1.id)
[%{id: :anthropic, name: "Anthropic"}, %{id: :openai, name: "OpenAI Updated"}]

resolver(opts \\ [])

@spec resolver(keyword()) :: (any(), any(), any() -> any())

Creates a configurable deep merge resolver function.

Returns a 3-arity function that can be passed to DeepMerge.deep_merge/3 with customizable behavior for list and special key handling.

Options

  • :union_list_keys - List of keys whose list values should be unioned (default: [])
  • :preserve_empty_list_keys - List of keys where empty list on right preserves left (default: [])

Examples

# Model merging with list unions
resolver = Merge.resolver(union_list_keys: [:aliases, :tags])
DeepMerge.deep_merge(base, override, resolver)

# Provider merging preserving exclude_models
resolver = Merge.resolver(preserve_empty_list_keys: [:exclude_models])
DeepMerge.deep_merge(base, override, resolver)