Packages
liter_llm
1.7.5
1.11.3
1.11.2
1.11.1
1.11.0
1.10.1
1.10.0
1.9.3
1.9.0
1.9.0-rc.2
1.9.0-rc.1
1.8.2
1.8.1
1.8.0
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.6.4
1.6.3
1.6.2
1.5.1
1.5.0
1.4.1
1.4.0-rc.61
1.4.0-rc.60
1.4.0-rc.59
1.4.0-rc.58
1.4.0-rc.57
1.4.0-rc.56
1.4.0-rc.55
1.4.0-rc.53
1.4.0-rc.52
1.4.0-rc.50
1.4.0-rc.48
1.4.0-rc.47
1.4.0-rc.46
1.4.0-rc.45
1.4.0-rc.44
1.4.0-rc.43
1.4.0-rc.39
1.4.0-rc.38
1.4.0-rc.37
1.4.0-rc.36
1.4.0-rc.35
1.4.0-rc.34
1.4.0-rc.33
1.4.0-rc.32
1.4.0-rc.31
1.4.0-rc.30
1.4.0-rc.27
1.4.0-rc.26
1.4.0-rc.25
1.4.0-rc.24
1.4.0-rc.22
1.4.0-rc.21
1.4.0-rc.20
1.4.0-rc.19
1.4.0-rc.18
1.4.0-rc.17
1.4.0-rc.16
1.4.0-rc.14
1.4.0-rc.13
1.4.0-rc.11
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
1.0.0-rc.9
1.0.0-rc.8
1.0.0-rc.7
1.0.0-rc.6
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
Universal LLM API client with Rust-powered polyglot bindings.
Current section
Files
Jump to
Current section
Files
lib/liter_llm/native.ex
# This file is auto-generated by alef — DO NOT EDIT.
# alef:hash:5f17ebe676822cfa77ea15c5d293e0cac18cf5c5fd2e4c3c2b8974a130abfd16
# To regenerate: alef generate
# To verify freshness: alef verify --exit-code
defmodule LiterLlm.Native do
@moduledoc false
use RustlerPrecompiled,
otp_app: :liter_llm,
crate: "liter_llm_nif",
base_url: "https://github.com/kreuzberg-dev/liter-llm/releases/download/v#{Mix.Project.config()[:version]}",
version: Mix.Project.config()[:version],
targets: [
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
"x86_64-pc-windows-gnu"
],
nif_versions: ["2.16", "2.17"],
force_build: System.get_env("LITER_LLM_BUILD") in ["1", "true"] or Mix.env() in [:dev]
@doc """
Create a new LLM client with simple scalar configuration.
This is the primary binding entry-point. All parameters except `api_key`
are optional — omitting them uses the same defaults as
[`ClientConfigBuilder`].
# Errors
Returns [`LiterLlmError`] if the underlying HTTP client cannot be
constructed, or if the resolved provider configuration is invalid.
"""
def create_client(_api_key, _base_url, _timeout_secs, _max_retries, _model_hint),
do: :erlang.nif_error(:nif_not_loaded)
@doc """
Create a new LLM client from a JSON string.
The JSON object accepts the same fields as `liter-llm.toml` (snake_case).
# Errors
Returns [`LiterLlmError::BadRequest`] if `json` is not valid JSON or
contains unknown fields.
"""
def create_client_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Encode bytes as a base64 data URL: `data:<mime>;base64,<b64>`.
`mime` defaults to [`IMAGE_PNG`] when `None`.
# Example
```
use liter_llm::image::{encode_data_url, IMAGE_PNG, IMAGE_JPEG};
let url = encode_data_url(b"\x89PNG", Some(IMAGE_PNG));
assert!(url.starts_with("data:image/png;base64,"));
let url_default = encode_data_url(b"\x89PNG", None);
assert!(url_default.starts_with("data:image/png;base64,"));
let jpeg_url = encode_data_url(b"\xff\xd8\xff", Some(IMAGE_JPEG));
assert!(jpeg_url.starts_with("data:image/jpeg;base64,"));
```
"""
def encode_data_url(_bytes, _mime), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Decode a base64 data URL into [`DecodedDataUrl`].
Returns `None` for:
- Non-data URLs (strings that do not start with `"data:"`).
- Malformed prefixes (missing `";base64,"` marker).
- Invalid base64 payloads.
The returned MIME string is extracted verbatim from the URL prefix —
it is not validated or normalised.
# Example
```
use liter_llm::image::{encode_data_url, decode_data_url, IMAGE_PNG};
let url = encode_data_url(b"hello", Some(IMAGE_PNG));
let decoded = decode_data_url(&url).expect("valid data URL");
assert_eq!(decoded.mime, IMAGE_PNG);
assert_eq!(decoded.data, b"hello");
// Non-data URLs return None.
assert!(decode_data_url("https://example.com/img.png").is_none());
// Missing ;base64, marker returns None.
assert!(decode_data_url("data:image/png,plaintext").is_none());
```
"""
def decode_data_url(_url), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Register a custom provider in the global runtime registry.
The provider will be checked **before** all built-in providers during model
detection. If a provider with the same `name` already exists it is replaced.
# Errors
Returns an error if the config is invalid (empty name, empty base_url, or
no model prefixes).
"""
def register_custom_provider(_config), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Remove a previously registered custom provider by name.
Returns `true` if a provider with the given name was found and removed,
`false` if no such provider existed.
# Errors
Returns an error if the custom-provider registry cannot be updated.
"""
def unregister_custom_provider(_name), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Return the capability flags for a named provider.
Performs an O(n) linear scan over the embedded registry (143 entries).
Returns an owned value so bindings can pass capability data without
borrowing registry internals.
For unknown `provider_name` values the function returns an all-`false`
sentinel so callers never need to handle `Option`.
"""
def capabilities(_provider_name), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Return all provider configs from the registry.
Useful for tooling, documentation generation, or runtime enumeration.
Returns the public [`ProviderConfig`] slice (without capability flags).
To query capability flags for a specific provider use [`capabilities`].
"""
def all_providers, do: :erlang.nif_error(:nif_not_loaded)
@doc """
Return the set of complex provider names.
Complex providers require custom auth/routing logic beyond simple bearer
tokens (e.g. AWS Bedrock SigV4, Vertex AI OAuth2).
The returned reference points into the static registry — no allocation.
"""
def complex_provider_names, do: :erlang.nif_error(:nif_not_loaded)
@doc """
Calculate the estimated cost of a completion given a model name and token
counts.
Returns `None` if the model is not present in the embedded pricing registry.
Returns `Some(cost_usd)` otherwise, where the value is in US dollars.
When an exact model name match is not found, progressively shorter prefixes
are tried by stripping from the last `-` or `.` separator. For example,
`gpt-4-0613` will match `gpt-4` if no `gpt-4-0613` entry exists.
# Example
```rust
use liter_llm::cost;
let usd = cost::completion_cost("gpt-4o", 1_000, 500).expect("gpt-4o is a known model");
// 1000 * 0.0000025 + 500 * 0.00001 = 0.0025 + 0.005 = 0.0075
assert!((usd - 0.0075).abs() < 1e-9);
```
"""
def completion_cost(_model, _prompt_tokens, _completion_tokens), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Calculate the estimated cost of a completion, accounting for cached
(cache-hit) prompt tokens billed at the provider's discounted rate.
`cached_tokens` is the count of prompt tokens served from the provider's
prompt cache. It must be `<= prompt_tokens` (cached tokens are a subset of
the prompt). The non-cached portion is billed at `input_cost_per_token`
and the cached portion at `cache_read_input_token_cost` when the model
has cache pricing; otherwise the entire prompt is billed at the regular
input rate.
Returns `None` if the model is not present in the embedded pricing
registry, mirroring [`completion_cost`].
"""
def completion_cost_with_cache(_model, _prompt_tokens, _cached_tokens, _completion_tokens),
do: :erlang.nif_error(:nif_not_loaded)
@doc """
Remove all guardrails from the global registry.
Primarily useful in tests to reset state between test cases.
# Panics
Panics if the global registry lock is poisoned.
"""
def clear, do: :erlang.nif_error(:nif_not_loaded)
@doc """
Count tokens in a text string using the tokenizer for the given model.
The tokenizer is resolved from the model name prefix (e.g. `"gpt-4o"` maps
to the `Xenova/gpt-4o` HuggingFace tokenizer). Tokenizers are cached after
first load.
# Errors
Returns [`LiterLlmError::BadRequest`] if the tokenizer cannot be loaded
(e.g. network failure on first use) or if tokenization itself fails.
"""
def count_tokens(_model, _text), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Count tokens for a full [`ChatCompletionRequest`].
Sums tokens across all message text contents plus a per-message overhead
of ~4 tokens (for role, separators, and formatting metadata). Tool
definitions and multimodal content parts (images, audio, documents) are
not counted — only textual content contributes to the token total.
# Errors
Returns [`LiterLlmError::BadRequest`] if the tokenizer cannot be loaded or
if tokenization fails for any message.
"""
def count_request_tokens(_model, _req), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Assert that `current_len + incoming` does not exceed `limit`.
Call this before appending `incoming` bytes to any buffer that must
stay below `limit`. Returns `Err(LiterLlmError::Streaming)` on overflow
and emits a `tracing::warn!` with context.
# Example
```ignore
check_bound("SSE buffer", buffer.len(), chunk.len(), SSE_BUFFER_MAX_BYTES)?;
buffer.push_str(chunk_str);
```
"""
def check_bound(_context, _current_len, _incoming, _limit), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Install the `ring` crypto provider as the rustls process default, idempotently.
rustls 0.23+ removed the implicit default provider. This function installs
`ring` once per process. Subsequent calls are no-ops. Calling it after
another rustls crypto provider has already been installed is safe: the
`Err` from `install_default()` is silently ignored.
Called automatically by every internal `reqwest::Client` constructor
(auth providers, default HTTP client). Bindings and downstream consumers
reach those constructors transitively, so no manual init is required.
WASM builds are exempt — the WASM target uses the browser/Node.js fetch
API instead of rustls, so no crypto provider is needed.
Windows builds use native-tls (SChannel) via reqwest, so rustls is not
present and no crypto provider installation is needed.
"""
def ensure_crypto_provider, do: :erlang.nif_error(:nif_not_loaded)
@doc """
Return the assistant's textual response, concatenating all `Text` parts
if the content is structured.
Returns `None` for `Refusal`-only or `OutputImage`-only responses.
"""
def assistantmessage_text(_obj), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Return the refusal message, if the model declined to respond.
Checks both the top-level `refusal` field and any `Refusal` parts
inside a structured `content`.
"""
def assistantmessage_refusal_text(_obj), do: :erlang.nif_error(:nif_not_loaded)
@doc "Return all [`AssistantPart::OutputImage`] parts in the response."
def assistantmessage_output_images(_obj), do: :erlang.nif_error(:nif_not_loaded)
@doc "Return all [`AssistantPart::OutputAudio`] parts in the response."
def assistantmessage_output_audio(_obj), do: :erlang.nif_error(:nif_not_loaded)
def waitforbatchconfig_default, do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_chat_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_embed_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_list_models_async(_obj), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_image_generate_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_speech_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_transcribe_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_moderate_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_rerank_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_search_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_ocr_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_create_file_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_retrieve_file_async(_obj, _file_id), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_delete_file_async(_obj, _file_id), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_list_files_async(_obj, _query), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_file_content_async(_obj, _file_id), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_create_batch_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_retrieve_batch_async(_obj, _batch_id), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_list_batches_async(_obj, _query), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_cancel_batch_async(_obj, _batch_id), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_fetch_batch_for_polling_async(_obj, _batch_id), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Poll a batch until it reaches a terminal status (Completed, Failed, Expired, Cancelled).
Uses exponential backoff with configurable initial interval, maximum interval, and backoff multiplier.
Optionally supports a timeout that aborts polling if exceeded.
# Errors
Returns `BatchWaitError::Failed` if the batch reaches a failure terminal status.
Returns `BatchWaitError::Timeout` if the configured timeout is exceeded.
Returns `BatchWaitError::Client` for underlying client errors.
# Example
```no_run
# use liter_llm::client::{DefaultClient, ClientConfig, WaitForBatchConfig};
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let client = DefaultClient::new(ClientConfig::new("api-key"), None)?;
let batch = client.wait_for_batch("b-123", WaitForBatchConfig::default()).await?;
println!("Batch completed: {:?}", batch.status);
# Ok(())
# }
```
"""
def defaultclient_wait_for_batch_async(_obj, _batch_id, _config), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_create_response_async(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_retrieve_response_async(_obj, _response_id), do: :erlang.nif_error(:nif_not_loaded)
def defaultclient_cancel_response_async(_obj, _response_id), do: :erlang.nif_error(:nif_not_loaded)
def budgetconfig_default, do: :erlang.nif_error(:nif_not_loaded)
def cacheconfig_default, do: :erlang.nif_error(:nif_not_loaded)
def ratelimitconfig_default, do: :erlang.nif_error(:nif_not_loaded)
@doc false
def defaultclient_chat_stream_start(_obj, _req), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def defaultclient_chat_stream_next(_handle), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def system_message_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def user_message_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def image_url_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def document_content_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def audio_content_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def assistant_message_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def tool_message_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def developer_message_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def function_message_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def chat_completion_tool_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def function_definition_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def tool_call_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def function_call_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def specific_tool_choice_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def specific_function_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def json_schema_format_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def usage_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def prompt_tokens_details_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def chat_completion_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def stream_options_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def chat_completion_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def choice_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def embedding_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def embedding_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def embedding_object_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def create_image_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def images_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def image_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def decoded_data_url_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def create_speech_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def create_transcription_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def transcription_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def transcription_segment_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def moderation_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def moderation_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def moderation_result_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def moderation_categories_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def moderation_category_scores_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def rerank_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def rerank_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def rerank_result_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def rerank_result_document_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def search_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def search_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def search_result_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def ocr_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def ocr_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def ocr_page_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def ocr_image_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def page_dimensions_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def models_list_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def model_object_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def create_file_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def file_object_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def file_list_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def file_list_query_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def delete_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def create_batch_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def batch_object_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def batch_request_counts_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def batch_list_response_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def batch_list_query_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def create_response_request_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def response_tool_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def response_object_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def response_output_item_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def response_usage_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def wait_for_batch_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def custom_provider_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def provider_capabilities_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def provider_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def auth_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def budget_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def cache_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def rate_limit_config_from_json(_json), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def literllmerror_status_code(_msg), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def literllmerror_is_transient(_msg), do: :erlang.nif_error(:nif_not_loaded)
@doc false
def literllmerror_error_type(_msg), do: :erlang.nif_error(:nif_not_loaded)
end