# AiSdkEx
Minimal Elixir AI SDK scaffolding for streaming text with tool calls.
## Environment
This project loads dotenv files automatically via `config/config.exs`.
Create a `.env` file (see `.env.example`) and set:
```
OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...
OPENCODE_API_KEY=...
```
## Examples
Run the streaming examples (OpenAI + Anthropic + OpenCode Zen, with tool calls):
```
bun run -- mix run examples/stream_text.exs
```
## Consuming the stream
The stream emits structured events. Here is the full pattern matching flow used in the example:
```elixir
case AI.stream_text(opts) do
{:ok, stream} ->
Enum.each(stream, fn
{:text_start, _id} ->
:ok
{:text_delta, _id, text} ->
IO.write(text)
{:text_end, _id} ->
IO.puts("")
{:tool_call, call} ->
IO.inspect(call, label: "tool_call")
{:tool_result, result} ->
IO.inspect(result, label: "tool_result")
{:finish, info} ->
IO.inspect(info, label: "finish")
{:error, error} ->
IO.inspect(error, label: "error")
{:raw, _} ->
:ok
other ->
IO.inspect(other, label: "event")
end)
other ->
IO.inspect(other, label: "stream_text")
end
```
For a minimal handler that only prints text and handles completion:
```elixir
case AI.stream_text(opts) do
{:ok, stream} ->
Enum.each(stream, fn
{:text_delta, _id, text} ->
IO.write(text)
{:finish, _info} ->
IO.puts("")
_ ->
:ok
end)
other ->
IO.inspect(other, label: "stream_text")
end
```
## Anthropic
Anthropic Messages-compatible streaming with the `claude-haiku-4-5` model:
```elixir
run.(
"Anthropic Messages (text)",
model: AI.Anthropic.messages("claude-haiku-4-5"),
prompt: "Say hello in one short sentence.",
max_tokens: 64
)
```
Tool calling works the same way:
```elixir
run.(
"Anthropic Messages (tool calling)",
model: AI.Anthropic.messages("claude-haiku-4-5"),
prompt:
"Use the add tool to add 7 and 5, then respond with the sum in one short sentence.",
tools: %{"add" => add_tool},
tool_choice: :required,
max_steps: 3,
max_tokens: 64
)
```
## OpenCode Zen
OpenCode Zen exposes OpenAI Responses-compatible and Anthropic Messages-compatible endpoints under the Zen base URL.
You can target Zen by passing `base_url` (and `api_key` if needed) in the model options:
```elixir
model: AI.OpenAI.responses(
"gpt-5.1-codex",
base_url: "https://opencode.ai/zen/v1",
api_key: System.get_env("OPENCODE_API_KEY")
)
```
```elixir
run.(
"OpenCode Zen (Anthropic Messages, text)",
model:
AI.Anthropic.messages(
"claude-haiku-4-5",
base_url: "https://opencode.ai/zen/v1",
api_key: System.get_env("OPENCODE_API_KEY")
),
prompt: "Say hello in one short sentence.",
max_tokens: 64
)
```
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `ai_sdk_ex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:ai_sdk_ex, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/ai_sdk_ex>.