Candil.Conversation (Candil v2.0.0)

Copy Markdown View Source

Conversation history management for Candil.Llm.

Maintains a message history and automatically manages context window limits. When the accumulated token estimate exceeds max_context_tokens, older messages are trimmed while always preserving the system prompt.

Usage

conv = Candil.Conversation.new(
  model: :llama3,
  system: "You are a helpful Elixir assistant.",
  max_context_tokens: 4096
)

{:ok, conv, response} = Candil.Conversation.chat(conv, "What is a GenServer?")
{:ok, conv, response} = Candil.Conversation.chat(conv, "Give me a code example.")

IO.puts(response.content)

Remote provider

conv = Candil.Conversation.new(
  model: gpt4o_model,
  provider: openai_provider,
  system: "You are a code reviewer.",
  max_context_tokens: 16_000
)

Token estimation

Token counts are estimated using a more accurate approximation that accounts for:

  • Per-message overhead (role, content wrapper): ~4 tokens
  • Per-message overhead for message arrays: ~3 tokens
  • Content itself: approximately ceil(byte_size / 4) for English text

The formula used is: 4 + ceil(content_bytes / 4) per message.

For more precise estimation, consider using a tokenizer library like tiktoken if available for your model.

Configuration

The following options can be set in application config:

config :candil, Candil.Conversation,
  max_tokens: 512,  # default max_tokens for responses
  estimation_mode: :default  # :default or :tiktoken (when available)

Summary

Functions

Returns the available context tokens (accounting for max_response_tokens).

Sends a user message and returns {:ok, updated_conv, response}.

Estimates tokens for text content.

Estimates tokens for a message, accounting for role and overhead.

Returns the full message list including the system prompt as the first message (if set).

Creates a new conversation.

Resets the conversation history, keeping the system prompt and config.

Returns the approximate token count for the current history.

Returns the number of turns (user+assistant pairs) in the conversation.

Types

message()

@type message() :: Candil.Inference.message()

t()

@type t() :: %Candil.Conversation{
  max_context_tokens: pos_integer(),
  max_response_tokens: pos_integer(),
  messages: [message()],
  model: atom() | Candil.Model.t(),
  opts: keyword(),
  provider: Candil.Provider.t() | nil,
  system: binary() | nil
}

Functions

available_context_tokens(conv)

@spec available_context_tokens(t()) :: non_neg_integer()

Returns the available context tokens (accounting for max_response_tokens).

chat(conv, user_message)

@spec chat(t(), binary()) :: {:ok, t(), Candil.Inference.response()} | {:error, any()}

Sends a user message and returns {:ok, updated_conv, response}.

Appends the user message to history, calls the model, appends the assistant response, and trims history if needed.

estimate_content_tokens(text)

@spec estimate_content_tokens(binary()) :: non_neg_integer()

Estimates tokens for text content.

Uses ceil(byte_size / 4) as a rough approximation for English text. Note: This is a rough estimate. For precise counts, use a tokenizer like tiktoken.

estimate_message_tokens(msg)

@spec estimate_message_tokens(message()) :: non_neg_integer()

Estimates tokens for a message, accounting for role and overhead.

The formula is:

  • Base overhead per message: ~4 tokens
  • Content: ceil(byte_size / 4) for typical English text

messages(conversation)

@spec messages(t()) :: [message()]

Returns the full message list including the system prompt as the first message (if set).

new(opts)

@spec new(keyword()) :: t()

Creates a new conversation.

Options

  • :model — atom alias (local engine) or Candil.Model struct (required)
  • :providerCandil.Provider struct for remote models
  • :system — system prompt (default: nil)
  • :max_context_tokens — approximate token limit for history (default: 4096)
  • :max_response_tokens — max tokens to generate in responses (default: 512)
  • Any other options are forwarded to Candil.chat/3 on each turn (:temperature, :max_tokens, etc.)

reset(conv)

@spec reset(t()) :: t()

Resets the conversation history, keeping the system prompt and config.

token_estimate(conv)

@spec token_estimate(t()) :: non_neg_integer()

Returns the approximate token count for the current history.

turn_count(conversation)

@spec turn_count(t()) :: non_neg_integer()

Returns the number of turns (user+assistant pairs) in the conversation.