Helper to create graph nodes that call an LLM.
Produces a node function that reads messages from state, sends them to the configured LLM provider, and appends the response to the messages list.
Summary
Functions
Reducer that accumulates token usage maps by summing numeric fields.
Returns a node function that calls an LLM provider.
One-shot structured extraction outside a graph node.
Returns a node function that asks the LLM for a structured result.
Validate a decoded structured result against a JSON-schema's top-level
required keys. Keys are compared as strings.
Functions
Reducer that accumulates token usage maps by summing numeric fields.
Use as the schema reducer for the usage key:
Graph.new(llm_usage: {%{}, &ChatModel.merge_usage/2})
Returns a node function that calls an LLM provider.
Options
:provider- module implementingLangEx.LLM(explicit):model- model string like"gpt-4o"or"claude-sonnet-4-20250514"(auto-resolves provider):messages_key- state key holding the message list (default::messages):usage_key- state key accumulating token usage (default::llm_usage); only written when the key exists in the graph state schema:tools- list of%LangEx.Tool{}definitions for function calling:resilient- route calls throughLangEx.LLM.Resilientfor retries with backoff.truefor defaults, or a keyword list ofResilientoptions (:max_retries,:retry_base_ms,:fallback, ...)- All other opts forwarded to
provider.chat/2(:api_key,:temperature, etc.)
Either :provider or :model must be given. When :model is a string and
:provider is absent, the provider is resolved via LangEx.LLM.Registry.init_chat_model/2.
Tool execution is handled by a separate LangEx.Tool.Node in the graph,
not by the LLM node itself.
Token usage accounting
When the provider implements chat_with_usage/2, token counts are
attached to the [:lang_ex, :llm, :chat, :stop] telemetry event as
:usage metadata. To also accumulate usage in graph state, declare
the usage key in the schema with merge_usage/2 as the reducer:
Graph.new(
messages: {[], &Message.add_messages/2},
llm_usage: {%{}, &ChatModel.merge_usage/2}
)Examples
Graph.add_node(:llm, ChatModel.node(model: "gpt-4o"))
Graph.add_node(:llm, ChatModel.node(model: "gpt-4o",
tools: [%LangEx.Tool{name: "search", ...}]
))
@spec structured( [LangEx.Message.t()], keyword() ) :: {:ok, map()} | {:error, term()}
One-shot structured extraction outside a graph node.
Forces the provider to answer via a synthetic respond tool whose
parameters are :schema, decodes the tool call (falling back to decoding
JSON content), and validates that the schema's top-level required keys
are present.
Options
:schema(required) - JSON-schema map describing the desired shape:resilient-trueorLangEx.LLM.Resilientoptions to retry on transient failures:provider/:modeland other options are forwarded to the provider
Returns {:ok, map} or {:error, reason}:
{:error, :no_structured_output}- the model returned nothing decodable{:error, {:missing_required, keys}}- required keys were absent{:error, term}- the provider call itself failed
Returns a node function that asks the LLM for a structured result.
The model is given a synthetic respond tool whose parameters are the
provided JSON-schema; calling it yields the structured data, which is
decoded and written to the :into state key (default :structured). A
clean assistant message carrying the JSON is appended to the messages so
the conversation stays valid. Works with any provider that supports tool
calling — no provider-specific configuration required.
Options
:schema(required) - JSON-schema map describing the desired shape:into- state key to write the decoded result to (default:structured):messages_key- state key holding the message list (default:messages):provider/:modeland other options are forwarded to the provider, exactly likenode/1
Example
Graph.add_node(:extract, ChatModel.structured_node(
model: "gpt-4o",
into: :analysis,
schema: %{
type: "object",
properties: %{sentiment: %{type: "string"}, score: %{type: "number"}},
required: ["sentiment", "score"]
}
))
Validate a decoded structured result against a JSON-schema's top-level
required keys. Keys are compared as strings.