ExAthena.ModelListing (ExAthena v0.18.0)

Copy Markdown View Source

Enumerates the models a backend can serve, normalised to ExAthena.Model.

This is the single place that knows how each family of backend answers "what models do you have?". It exists as its own module rather than inside ExAthena.Providers.ReqLLM because listing shares nothing with inference: it speaks different endpoints, on different paths, with different failure tolerances.

Why the transport is hand-rolled

Neither req_llm nor llm_db can enumerate a live server. req_llm ships no discovery callback and no adapter that issues GET /v1/models; its ReqLLM.available_models/1 is a catalog query (llm_db candidates filtered by which credentials happen to resolve), and llm_db is a build-time snapshot with no runtime HTTP at all. Neither has any notion of a local Ollama daemon — the catalog carries ollama_cloud, never localhost:11434. So live enumeration is ours to do, and the catalog is used for what it is genuinely good at: metadata.

Dispatch

BackendSource
:ollamaGET /api/tags on the bare host, optionally plus the cloud catalogue
:llamacppGET /v1/models
:exoGET /v1/models?status=downloaded
any other tag with a base_urlGET /v1/models (OpenAI wire format)
any other tagthe llm_db catalog for that provider

Local backends are matched on :openai_compatible_backend rather than on :req_llm_provider_tag, because every OpenAI-wire backend — Ollama and llama.cpp included — carries the tag "openai"; the tag cannot tell them apart.

Summary

Functions

Reduce a listing result to the bare sorted ids / error atoms that the deprecated ExAthena.Chat.* helpers promised.

List the models available for the backend described by opts.

Functions

legacy_result(arg)

@spec legacy_result({:ok, [ExAthena.Model.t()]} | {:error, ExAthena.Error.t()}) ::
  {:ok, [String.t()]} | {:error, atom() | {:http, integer()}}

Reduce a listing result to the bare sorted ids / error atoms that the deprecated ExAthena.Chat.* helpers promised.

Lives here, next to the errors it decodes, so the three shims share one translation instead of re-deriving it — the canonical ExAthena.Error keeps the original atom in :raw for exactly this purpose.

list(opts \\ [])

@spec list(keyword()) :: {:ok, [ExAthena.Model.t()]} | {:error, ExAthena.Error.t()}

List the models available for the backend described by opts.

opts are the provider opts ExAthena.Config already threads for a call — :base_url, :api_key, :req_llm_provider_tag, :openai_compatible_backend, :extra_headers — plus:

  • :timeout_ms — per-request receive timeout (default 2_000). Listing backs a UI picker, so it fails fast rather than hanging the interface.
  • :include_cloud — Ollama only; also fetch the ollama.com catalogue.
  • :cloud_base_url — Ollama cloud host override (default https://ollama.com).