LemonAi (lemon_ai v0.1.0)
View SourceProvider-agnostic LLM client: one API for 27 providers.
This module is the whole public surface. stream/3 and complete/3 send a
LemonAi.Types.Context to any provider's model and return either a lazy
LemonAi.EventStream of events or the final LemonAi.Types.AssistantMessage. Model
configuration, cost accounting, per-provider circuit breaking and rate limiting,
and context handoffs between providers are all handled behind this interface.
Usage
# Create a context
context = LemonAi.new_context(system_prompt: "You are a helpful assistant")
context = LemonAi.Types.Context.add_user_message(context, "Hello!")
# Stream a response
{:ok, stream} = LemonAi.stream(model, context)
for event <- LemonAi.EventStream.events(stream) do
case event do
{:text_delta, _idx, delta, _partial} -> IO.write(delta)
{:done, _reason, message} -> IO.puts("\nDone!")
_ -> :ok
end
end
# Or get a complete response
{:ok, message} = LemonAi.complete(model, context)Models
Models are defined with their provider, API type, and capabilities:
model = %LemonAi.Types.Model{
id: "claude-sonnet-4-20250514",
name: "Claude Sonnet 4",
api: :anthropic_messages,
provider: :anthropic,
base_url: "https://api.anthropic.com",
reasoning: true,
input: [:text, :image],
cost: %{input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75},
context_window: 200_000,
max_tokens: 64_000
}
Summary
Functions
Calculate the cost of a response based on model pricing.
Get a complete response from an LLM (non-streaming).
Extract all text content from an assistant message.
Extract all thinking content from an assistant message.
Extract all tool calls from an assistant message.
Stream a response from an LLM.
Functions
@spec calculate_cost(LemonAi.Types.Model.t(), LemonAi.Types.Usage.t()) :: LemonAi.Types.Cost.t()
Calculate the cost of a response based on model pricing.
@spec complete( LemonAi.Types.Model.t(), LemonAi.Types.Context.t(), LemonAi.Types.StreamOptions.t() | map() ) :: {:ok, LemonAi.Types.AssistantMessage.t()} | {:error, term()}
Get a complete response from an LLM (non-streaming).
This is a convenience wrapper around stream/3 that collects
all events and returns the final message.
Examples
{:ok, message} = LemonAi.complete(model, context)
IO.puts(LemonAi.get_text(message))
@spec get_text(LemonAi.Types.AssistantMessage.t()) :: String.t()
Extract all text content from an assistant message.
Examples
{:ok, message} = LemonAi.complete(model, context)
text = LemonAi.get_text(message)
@spec get_thinking(LemonAi.Types.AssistantMessage.t()) :: String.t()
Extract all thinking content from an assistant message.
@spec get_tool_calls(LemonAi.Types.AssistantMessage.t()) :: [ LemonAi.Types.ToolCall.t() ]
Extract all tool calls from an assistant message.
@spec stream( LemonAi.Types.Model.t(), LemonAi.Types.Context.t(), LemonAi.Types.StreamOptions.t() | map() ) :: {:ok, LemonAi.EventStream.t()} | {:error, term()}
Stream a response from an LLM.
Returns an EventStream that emits events as the response is generated.
Use EventStream.events/1 to consume events or EventStream.result/1
to wait for the final message.
Options
:temperature- Sampling temperature (0.0-2.0):max_tokens- Maximum tokens to generate:api_key- Override the default API key:headers- Additional HTTP headers:reasoning- Thinking level (:minimal, :low, :medium, :high, :xhigh)
Examples
{:ok, stream} = LemonAi.stream(model, context, %{temperature: 0.7})
stream
|> LemonAi.EventStream.events()
|> Enum.each(&IO.inspect/1)