View Source Anthropix (Anthropix v0.1.0)
Anthropix is an open-source Elixir client for the Anthropic API, providing a simple and convenient way to integrate Claude, Anthropic's powerful language model, into your applications.
- ✅ API client fully implementing the Anthropic API
- 🛜 Streaming API requests
- Stream to an Enumerable
- Or stream messages to any Elixir process
- 😎 Powerful yet painless function calling with Agents
Installation
The package can be installed by adding anthropix
to your list of
dependencies in mix.exs
.
def deps do
[
{:anthropix, "~> 0.1.0"}
]
end
Quickstart
For more examples, refer to the Anthropix documentation.
Initiate a client.
See Anthropix.init/2
.
iex> client = Anthropix.init(api_key)
Chat with Claude
See Anthropix.chat/2
.
iex> messages = [
...> %{role: "system", content: "You are a helpful assistant."},
...> %{role: "user", content: "Why is the sky blue?"},
...> %{role: "assistant", content: "Due to rayleigh scattering."},
...> %{role: "user", content: "How is that different than mie scattering?"},
...> ]
iex> Anthropix.chat(client, [
...> model: "claude-3-opus-20240229",
...> messages: messages,
...> ])
{:ok, %{"content" => [%{
"type" => "text",
"text" => "Mie scattering affects all wavelengths similarly, while Rayleigh favors shorter ones."
}], ...}}
Streaming
A streaming request can be initiated by setting the :stream
option.
When :stream
is true a lazy Enumerable.t/0
is returned which can be used
with any Stream
functions.
iex> {:ok, stream} = Anthropix.chat(client, [
...> model: "claude-3-opus-20240229",
...> messages: messages,
...> stream: true,
...> ])
{:ok, #Function<52.53678557/2 in Stream.resource/3>}
iex> stream
...> |> Stream.each(&update_ui_with_chunk/1)
...> |> Stream.run()
:ok
Because the above approach builds the Enumerable.t/0
by calling receive
,
using this approach inside GenServer callbacks may cause the GenServer to
misbehave. Setting the :stream
option to a pid/0
returns a Task.t/0
which will send messages to the specified process.
Function calling
Chatting with Claude is nice and all, but when it comes to function calling,
Anthropix has a trick up its sleeve. Meet Anthropix.Agent
.
The Agent module abstracts away all the rough bits of implementing Anthropic style function calling, leaving a delightfully simple API that opens the doors to powerful and advanced agent workflows.
iex> ticker_tool = %Anthropix.Tool.new([
...> name: "get_ticker_symbol",
...> description: "Gets the stock ticker symbol for a company searched by name. Returns str: The ticker symbol for the company stock. Raises TickerNotFound: if no matching ticker symbol is found.",
...> params: [
...> %{name: "company_name", description: "The name of the company.", type: "string"}
...> ],
...> function: &MyStocks.get_ticker/1
...> ])
iex> price_tool = %Anthropix.Tool.new([
...> name: "get_current_stock_price",
...> description: "Gets the current stock price for a company. Returns float: The current stock price. Raises ValueError: if the input symbol is invalid/unknown.",
...> params: [
...> %{name: "symbol", description: "The stock symbol of the company to get the price for.", type: "string"}
...> ],
...> function: &MyStocks.get_price/1
...> ])
iex> agent = Anthropix.Agent.init(
...> Anthropix.init(api_key),
...> [ticker_tool, price_tool]
...> )
iex> Anthropix.Agent.chat(agent, [
...> model: "claude-3-sonnet-20240229",
...> system: "Answer like Snoop Dogg.",
...> messages: [
...> %{role: "user", content: "What is the current stock price of General Motors?"}
...> ]
...> ])
%{
result: %{
"content" => [%{
"type" => "text",
"text" => "*snaps fingers* Damn shawty, General Motors' stock is sittin' pretty at $39.21 per share right now. Dat's a fly price for them big ballers investin' in one of Detroit's finest auto makers, ya heard? *puts hands up like car doors* If ya askin' Snoop, dat stock could be rollin' on some dubs fo' sho'. Just don't get caught slippin' when them prices dippin', ya dig?"
}]
}
}
For a more detailed walkthrough, refer to the Anthropix.Agent
documentation.
Summary
Types
@type client() :: %Anthropix{req: Req.Request.t()}
Client struct
@type content_block() :: %{ :type => String.t(), optional(:text) => String.t(), optional(:source) => %{ type: String.t(), media_type: String.t(), data: String.t() } }
Message content block.
@type message() :: %{role: String.t(), content: String.t() | [content_block()]}
Chat message
A chat message is a map/0
with the following fields:
:role
(String.t/0
) - Required. The role of the message, eitheruser
orassistant
.:content
- Required. Message content, either a single string or an array of content blocks.
@type response() :: {:ok, map() | Enumerable.t() | Task.t()} | {:error, term()}
Client response
Functions
Chat with Claude. Send a list of structured input messages with text and/or image content, and Claude will generate the next message in the conversation.
Options
:model
(String.t/0
) - Required. The model that will complete your prompt.:messages
(list ofmap/0
) - Required. Input messages.:system
(String.t/0
) - System prompt.:max_tokens
(integer/0
) - The maximum number of tokens to generate before stopping. The default value is4096
.:metadata
(map/0
) - A map describing metadata about the request.:stop_sequences
(list ofString.t/0
) - Custom text sequences that will cause the model to stop generating.:stream
- Whether to incrementally stream the response using server-sent events. The default value isfalse
.:tools
(list of struct of type Anthropix.Tool) - A list of tools the model may call.:temperature
(float/0
) - Amount of randomness injected into the response.:top_k
(integer/0
) - Only sample from the top K options for each subsequent token.:top_p
(float/0
) - Amount of randomness injected into the response.
Message structure
Each message is a map with the following fields:
:role
(String.t/0
) - Required. The role of the message, eitheruser
orassistant
.:content
- Required. Message content, either a single string or an array of content blocks.
Examples
iex> messages = [
...> %{role: "system", content: "You are a helpful assistant."},
...> %{role: "user", content: "Why is the sky blue?"},
...> %{role: "assistant", content: "Due to rayleigh scattering."},
...> %{role: "user", content: "How is that different than mie scattering?"},
...> ]
iex> Anthropix.chat(client, [
...> model: "claude-3-opus-20240229",
...> messages: messages,
...> ])
{:ok, %{"content" => [%{
"type" => "text",
"text" => "Mie scattering affects all wavelengths similarly, while Rayleigh favors shorter ones."
}], ...}}
# Passing true to the :stream option initiates an async streaming request.
iex> Anthropix.chat(client, [
...> model: "claude-3-opus-20240229",
...> messages: messages,
...> stream: true,
...> ])
{:ok, #Function<52.53678557/2 in Stream.resource/3>}
@spec init() :: client()
Calling init/1
without passing an API key, creates a new Anthropix API
client using the API key set in your application's config.
config :anthropix, :api_key, "sk-ant-your-key"
If given, a keyword list of options will be passed to Req.new/1
.
Examples
iex> client = Anthropix.init()
%Anthropix{}
Calling init/2
with an API key creates a new Anthropix API client, using the
given API key. Optionally, a keyword list of options can be passed through to
Req.new/1
.
Examples
iex> client = Anthropix.init("sk-ant-your-key", receive_timeout: :infinity)
%Anthropix{}