LLMDB.Spec (LLM DB v2025.12.4)

View Source

Canonical "provider:model" spec parsing and resolution.

This module provides functions to parse and resolve model specifications in various formats, including "provider:model" strings, "model@provider" strings (filename-safe), tuples, and bare model IDs with provider scope.

String Formats

Two string formats are supported:

  • "provider:model" - Traditional colon separator (default)
  • "model@provider" - Email-like format, filesystem-safe for filenames

Both formats parse to the same internal representation and can be used interchangeably. The @ format is recommended when model specs are used in filenames, CI artifact names, or other filesystem contexts.

Amazon Bedrock Inference Profiles

For Amazon Bedrock models, inference profile IDs with region prefixes (us., eu., ap., ca., global.) are supported. The region prefix is stripped for catalog lookup but preserved in the returned model ID. For example:

iex> LLMDB.Spec.resolve("bedrock:us.anthropic.claude-opus-4-1-20250805-v1:0")
{:ok, {:bedrock, "us.anthropic.claude-opus-4-1-20250805-v1:0", %LLMDB.Model{}}}

The lookup uses "anthropic.claude-opus-4-1-20250805-v1:0" to find metadata, but the returned model ID retains the "us." prefix for API routing purposes.

Summary

Functions

Builds a model specification string from various inputs.

Formats a model specification as a string.

Normalizes a model specification to tuple format.

Parses and validates a provider identifier.

Parses a model specification string in either "provider:model" or "model@provider" format.

Parses a model specification string, raising on error.

Resolves a model specification to a canonical model record.

Functions

build_spec(input, opts \\ [])

@spec build_spec(
  String.t() | {atom(), String.t()},
  keyword()
) :: String.t()

Builds a model specification string from various inputs.

Accepts strings (in any supported format) or tuples and outputs a string in the desired format.

Parameters

  • input - Model spec as string or tuple
  • opts - Keyword list with optional :format for output format

Examples

iex> LLMDB.Spec.build_spec("openai:gpt-4", format: :filename_safe)
"gpt-4@openai"

iex> LLMDB.Spec.build_spec({:openai, "gpt-4"}, format: :model_at_provider)
"gpt-4@openai"

format_spec(arg, format \\ nil)

@spec format_spec(
  {atom(), String.t()},
  atom() | nil
) :: String.t()

Formats a model specification as a string.

Converts a {provider, model_id} tuple to string format. The output format can be controlled via the format parameter or falls back to the application config :llm_db, :model_spec_format (default: :provider_colon_model).

Parameters

  • spec - {provider_atom, model_id} tuple
  • format - Optional format override (atom)

Supported Formats

  • :provider_colon_model - "provider:model" (default)
  • :model_at_provider - "model@provider" (filename-safe)
  • :filename_safe - alias for :model_at_provider

Examples

iex> LLMDB.Spec.format_spec({:openai, "gpt-4"})
"openai:gpt-4"

iex> LLMDB.Spec.format_spec({:openai, "gpt-4"}, :model_at_provider)
"gpt-4@openai"

iex> LLMDB.Spec.format_spec({:openai, "gpt-4o-mini"}, :filename_safe)
"gpt-4o-mini@openai"

normalize_spec(input)

@spec normalize_spec(String.t() | {atom(), String.t()}) :: {atom(), String.t()}

Normalizes a model specification to tuple format.

Accepts either a string (in any supported format) or a tuple and returns a normalized {provider, model_id} tuple.

Examples

iex> LLMDB.Spec.normalize_spec("openai:gpt-4")
{:openai, "gpt-4"}

iex> LLMDB.Spec.normalize_spec("gpt-4@openai")
{:openai, "gpt-4"}

iex> LLMDB.Spec.normalize_spec({:openai, "gpt-4"})
{:openai, "gpt-4"}

parse_provider(input)

@spec parse_provider(atom() | binary()) ::
  {:ok, atom()} | {:error, :unknown_provider | :bad_provider}

Parses and validates a provider identifier.

Accepts atom or binary input, normalizes to atom, and verifies the provider exists in the current catalog.

Parameters

  • input - Provider identifier as atom or binary

Returns

  • {:ok, atom} - Normalized provider atom if valid and exists in catalog
  • {:error, :unknown_provider} - Provider not found in catalog
  • {:error, :bad_provider} - Invalid provider format

Examples

iex> LLMDB.Spec.parse_provider(:openai)
{:ok, :openai}

iex> LLMDB.Spec.parse_provider("google-vertex")
{:ok, :google_vertex}

iex> LLMDB.Spec.parse_provider("nonexistent")
{:error, :unknown_provider}

parse_spec(input, opts \\ [])

@spec parse_spec(
  String.t() | {atom(), String.t()},
  keyword()
) ::
  {:ok, {atom(), String.t()}}
  | {:error,
     :invalid_format
     | :ambiguous_format
     | :unknown_provider
     | :bad_provider
     | :invalid_chars
     | :empty_segment}

Parses a model specification string in either "provider:model" or "model@provider" format.

Automatically detects the format based on separators present. Validates the provider exists in the catalog and checks for reserved characters in segments.

Parameters

  • spec - String in "provider:model" or "model@provider" format, or {provider, model_id} tuple
  • opts - Keyword list with optional :format to explicitly specify format

Options

  • :format - Explicitly specify the format as :colon or :at. Required when both separators present.

Returns

  • {:ok, {provider_atom, model_id}} - Parsed and normalized spec
  • {:error, :invalid_format} - No valid separator found
  • {:error, :ambiguous_format} - Both separators present without explicit format
  • {:error, :unknown_provider} - Provider not found in catalog
  • {:error, :bad_provider} - Invalid provider format
  • {:error, :invalid_chars} - Reserved characters in provider or model segments
  • {:error, :empty_segment} - Provider or model segment is empty

Examples

iex> LLMDB.Spec.parse_spec("openai:gpt-4")
{:ok, {:openai, "gpt-4"}}

iex> LLMDB.Spec.parse_spec("gpt-4@openai")
{:ok, {:openai, "gpt-4"}}

iex> LLMDB.Spec.parse_spec("google-vertex:gemini-pro")
{:ok, {:google_vertex, "gemini-pro"}}

iex> LLMDB.Spec.parse_spec("provider:model@ambiguous", format: :colon)
{:ok, {:provider, "model@ambiguous"}}

iex> LLMDB.Spec.parse_spec("gpt-4")
{:error, :invalid_format}

parse_spec!(input, opts \\ [])

@spec parse_spec!(
  String.t() | {atom(), String.t()},
  keyword()
) :: {atom(), String.t()}

Parses a model specification string, raising on error.

Same as parse_spec/2 but raises ArgumentError instead of returning error tuple.

Examples

iex> LLMDB.Spec.parse_spec!("openai:gpt-4")
{:openai, "gpt-4"}

iex> LLMDB.Spec.parse_spec!("gpt-4@openai")
{:openai, "gpt-4"}

resolve(input, opts \\ [])

@spec resolve(
  String.t() | {atom(), String.t()},
  keyword()
) :: {:ok, {atom(), String.t(), LLMDB.Model.t()}} | {:error, term()}

Resolves a model specification to a canonical model record.

Accepts multiple input formats:

  • "provider:model" string
  • {provider, model_id} tuple
  • Bare "model" string with opts[:scope] = provider_atom

Handles alias resolution and validates the model exists in the catalog.

Parameters

  • input - Model specification in one of the supported formats
  • opts - Keyword list with optional :scope for bare model resolution

Returns

  • {:ok, {provider, canonical_id, Model.t()}} - Resolved model
  • {:error, :not_found} - Model doesn't exist
  • {:error, :ambiguous} - Bare model ID exists under multiple providers without scope
  • {:error, :invalid_format} - Malformed input
  • {:error, term} - Other parsing errors

Examples

iex> LLMDB.Spec.resolve("openai:gpt-4")
{:ok, {:openai, "gpt-4", %LLMDB.Model{}}}

iex> LLMDB.Spec.resolve({:openai, "gpt-4"})
{:ok, {:openai, "gpt-4", %LLMDB.Model{}}}

iex> LLMDB.Spec.resolve("gpt-4", scope: :openai)
{:ok, {:openai, "gpt-4", %LLMDB.Model{}}}

iex> LLMDB.Spec.resolve("gpt-4")
{:error, :ambiguous}