Packages
llm_composer
0.12.2
0.20.2
0.20.1
0.20.0
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.1
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.3
0.12.2
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.8.0
0.7.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
LlmComposer is an Elixir library that facilitates chat interactions with language models, providing tools to handle user messages, generate responses, and execute functions automatically based on model outputs.
Current section
Files
Jump to
Current section
Files
lib/llm_composer/cost/fetchers/models_dev.ex
defmodule LlmComposer.Cost.Fetchers.ModelsDev do
@moduledoc """
models.dev-specific pricing fetcher for OpenAI and Google providers.
Fetches pricing information from the models.dev API dataset for OpenAI and Google
models. Uses 24-hour caching to minimize API calls and improve performance.
## Supported Providers
- `:open_ai` - OpenAI models (GPT series)
- `:google` - Google Gemini models
## Implementation Notes
models.dev provides a single consolidated dataset (api.json) containing pricing
for multiple providers. The entire dataset is cached to avoid repeated downloads.
"""
alias LlmComposer.HttpClient
require Logger
@cache_mod Application.compile_env(:llm_composer, :cache_mod, LlmComposer.Cache.Ets)
@models_dev_url "https://models.dev/"
@cache_key "models_dev_api"
@default_cache_ttl_in_hours 24
@spec fetch_pricing(atom(), String.t()) :: map() | nil
def fetch_pricing(provider, model) when provider in [:open_ai, :google] do
case fetch_with_cache() do
{:ok, data} -> extract_pricing_from_data(data, provider, model)
:error -> nil
end
rescue
e ->
Logger.error(
"Error fetching pricing from models.dev for provider=#{provider} model=#{model}: #{Exception.message(e)}"
)
nil
end
def fetch_pricing(_provider, _model), do: nil
defp fetch_with_cache do
case @cache_mod.get(@cache_key) do
{:ok, cached_data} ->
Logger.debug("models.dev cache hit")
{:ok, cached_data}
:miss ->
Logger.debug("models.dev cache miss")
fetch_and_cache_data()
end
end
defp fetch_and_cache_data do
client = HttpClient.client(@models_dev_url, [])
case Tesla.get(client, "/api.json") do
{:ok, %{status: 200, body: data}} ->
ttl =
Application.get_env(:llm_composer, :cache_ttl, @default_cache_ttl_in_hours * 60 * 60)
@cache_mod.put(@cache_key, data, ttl)
{:ok, data}
{:ok, %{status: status}} ->
Logger.warning("models.dev API returned status #{status}")
:error
{:error, reason} ->
Logger.warning("Failed to fetch from models.dev API: #{inspect(reason)}")
:error
end
end
defp extract_pricing_from_data(data, provider, model) do
provider_key =
provider
|> Atom.to_string()
|> String.replace("_", "")
case get_in(data, [provider_key, "models", model, "cost"]) do
%{"input" => input, "output" => output} ->
Logger.debug(
"Extracted pricing for #{provider_key}/#{model}: input=$#{input}/M, output=$#{output}/M"
)
%{
input_price_per_million:
input
|> to_string()
|> Decimal.new(),
output_price_per_million:
output
|> to_string()
|> Decimal.new()
}
nil ->
Logger.debug("No pricing found for #{provider_key}/#{model} in models.dev data")
nil
invalid_cost ->
Logger.warning(
"Invalid cost structure for #{provider_key}/#{model}: #{inspect(invalid_cost)}"
)
nil
end
end
end