LemonAi (lemon_ai v0.1.0)

View Source

Provider-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

calculate_cost(model, usage)

Calculate the cost of a response based on model pricing.

complete(model, context, opts \\ %{})

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))

get_text(assistant_message)

Extract all text content from an assistant message.

Examples

{:ok, message} = LemonAi.complete(model, context)
text = LemonAi.get_text(message)

get_thinking(assistant_message)

@spec get_thinking(LemonAi.Types.AssistantMessage.t()) :: String.t()

Extract all thinking content from an assistant message.

get_tool_calls(assistant_message)

Extract all tool calls from an assistant message.

new_context(opts \\ [])

See LemonAi.Types.Context.new/1.

stream(model, context, opts \\ %{})

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)