# `LLMDB.Validate`
[🔗](https://github.com/agentjido/llm_db/blob/main/lib/llm_db/engine/validate.ex#L1)

Validation functions for providers and models using Zoi schemas.

Provides functions to validate individual records or batches of records,
handling errors gracefully and ensuring catalog viability.

# `validation_error`

```elixir
@type validation_error() :: term()
```

# `ensure_viable`

```elixir
@spec ensure_viable([LLMDB.Provider.t()], [LLMDB.Model.t()]) ::
  :ok | {:error, :empty_catalog}
```

Ensures that we have at least one provider and one model for a viable catalog.

Returns :ok if both lists are non-empty, otherwise returns an error.

## Examples

    iex> ensure_viable([%{id: :openai}], [%{id: "gpt-4o", provider: :openai}])
    :ok

    iex> ensure_viable([], [%{id: "gpt-4o", provider: :openai}])
    {:error, :empty_catalog}

    iex> ensure_viable([%{id: :openai}], [])
    {:error, :empty_catalog}

# `validate_model`

```elixir
@spec validate_model(map()) :: {:ok, LLMDB.Model.t()} | {:error, validation_error()}
```

Validates a single model map against the Model schema.

## Examples

    iex> validate_model(%{id: "gpt-4o", provider: :openai})
    {:ok, %{id: "gpt-4o", provider: :openai, deprecated: false, aliases: []}}

    iex> validate_model(%{id: "gpt-4o"})
    {:error, _}

# `validate_model_overlay`

```elixir
@spec validate_model_overlay(map()) :: {:ok, map()} | {:error, validation_error()}
```

Validates a model map and returns a sparse overlay that preserves only the
explicitly supplied fields plus required identity.

# `validate_models`

```elixir
@spec validate_models([map()]) :: {:ok, [LLMDB.Model.t()], non_neg_integer()}
```

Validates a list of model maps, collecting valid ones and counting invalid.

Returns all valid models and the count of invalid ones that were dropped.

## Examples

    iex> models = [
    ...>   %{id: "gpt-4o", provider: :openai},
    ...>   %{id: :invalid, provider: :openai},
    ...>   %{id: "claude-3", provider: :anthropic}
    ...> ]
    iex> validate_models(models)
    {:ok, [%{id: "gpt-4o", ...}, %{id: "claude-3", ...}], 1}

# `validate_models_for_merge`

```elixir
@spec validate_models_for_merge([map()]) :: {:ok, [map()], non_neg_integer()}
```

Validates a list of model maps and returns sparse overlays suitable for merge.

# `validate_provider`

```elixir
@spec validate_provider(map()) ::
  {:ok, LLMDB.Provider.t()} | {:error, validation_error()}
```

Validates a single provider map against the Provider schema.

## Examples

    iex> validate_provider(%{id: :openai})
    {:ok, %{id: :openai}}

    iex> validate_provider(%{id: "openai"})
    {:error, _}

# `validate_provider_overlay`

```elixir
@spec validate_provider_overlay(map()) :: {:ok, map()} | {:error, validation_error()}
```

Validates a provider map and returns a sparse overlay that preserves only the
explicitly supplied fields plus required identity.

# `validate_providers`

```elixir
@spec validate_providers([map()]) :: {:ok, [LLMDB.Provider.t()], non_neg_integer()}
```

Validates a list of provider maps, collecting valid ones and counting invalid.

Returns all valid providers and the count of invalid ones that were dropped.

## Examples

    iex> providers = [%{id: :openai}, %{id: "invalid"}, %{id: :anthropic}]
    iex> validate_providers(providers)
    {:ok, [%{id: :openai}, %{id: :anthropic}], 1}

# `validate_providers_for_merge`

```elixir
@spec validate_providers_for_merge([map()]) :: {:ok, [map()], non_neg_integer()}
```

Validates a list of provider maps and returns sparse overlays suitable for merge.

# `validate_runtime_contract`

```elixir
@spec validate_runtime_contract([LLMDB.Provider.t()], [LLMDB.Model.t()]) ::
  :ok | {:error, {:invalid_runtime_contract, [map()]}}
```

Validates typed provider runtime and model execution metadata after merge/enrichment.

This validator is migration-safe:
- providers without `runtime` are accepted for now
- models without `execution` are accepted for now
- once typed metadata is present, invalid declarations fail
- `catalog_only: true` opts a provider or model out of execution requirements

---

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