|> GenAI
Compose model conversations in Elixir
Chat, stream, call tools and classify conversation state through a shared thread API. Fork independent branches, then compare or combine their results explicitly.
Forks, classifiers and native DashScope are available in merged source. Install the source preview or choose the published Hex v0.5.0 package.
GenAI.chat(:session)
|> GenAI.with_model(GenAI.Provider.Anthropic.Models.claude_sonnet())
|> GenAI.with_message(GenAI.Message.user("Explain quantum computing simply."))
|> GenAI.run()
Provider-specific APIs. Shared thread protocols.
Why GenAI
|> Automatic format mapping
Choose a model with its own encoder: OpenAI-compatible, Anthropic Messages or native Alibaba DashScope. Re-encode supported messages and tools for the selected API; model capabilities still determine which options are accepted.
|> Bounded tool loops
Register application tools once, then use run_with_tools to execute calls, append correlated results and continue inference. Set iteration and call limits per branch, with shared fork concurrency bounds.
|> Media generation
Text-to-image, speech synthesis and transcription through GenAI.Media.Request and generate_media/1. The media router selects a provider by its declared capabilities.
|> Provider-agnostic threads
Conversation threads are provider-independent graph structures. Swap with_model/2 and re-run — the full message history re-encodes for the new provider automatically.
|> Local inference
Connect Ollama for local chat, or install ExLLama separately for text-only Jev-Omni classification through llama.cpp. Native build tools, a compatible GGUF and the exported decision head are separate requirements.
|> OTP-native concurrency
Forks are conversation threads. Run or stream every leaf with a global concurrency limit, keep each branch's settings and history, and identify results and stream events by their full branch path.
|> Extract, aggregate, transform
Extract the last message, JSON key/index paths or fenced blocks; aggregate across a conversation; or request validated structured extraction from a chat model. Transform the continuing thread explicitly.
|> Typed classifier questions
Ask boolean, ordered-choice or numeric-rubric questions with GenAI.classify. Keep selected probabilities, provider confidence and expected scores distinct. Classify directly or inside a conversation pipeline.
|> Hosted and local decisions
Use TypeSafe's hosted Jev, Alibaba's decision-model-preview through System One, or the independent akhilaaa3/Jev-Omni checkpoint through ExLLama. These integrations currently execute text requests.
In practice — latest merged source
The examples below use features merged to develop. They are not yet a new Hex release; use the pinned source install below.
alias GenAI.Provider.{Anthropic, Qwen}
thread =
GenAI.chat(:session)
|> GenAI.with_message(GenAI.Message.user("Summarize this ticket."))
# Configure credentials for the provider you choose.
GenAI.with_model(thread, Anthropic.Models.claude_sonnet())
GenAI.with_model(thread, Qwen.Models.model("qwen-plus"))
GenAI.with_model(thread, Qwen.Models.native("qwen-plus"))
# Native DashScope additionally needs a matching native_base_url.
thread =
GenAI.chat(:session)
|> GenAI.with_model(GenAI.Provider.Qwen.Models.model("qwen-plus"))
|> GenAI.with_message(GenAI.Message.user("Suggest a clear refund policy."))
fork = GenAI.Pipeline.split(thread, 3)
{:ok, {results, updated_fork}} =
GenAI.run(fork, nil, max_concurrency: 2)
{:ok, completion} = GenAI.Thread.Fork.Result.fetch(results, [0])
# Join or select branches explicitly; updated_fork keeps all continuations.
alias GenAI.Classifier.Question
alias GenAI.Pipeline
ticket =
GenAI.chat(:session)
|> GenAI.with_message(GenAI.Message.user("I was charged twice."))
# Set JEV_API_KEY for TypeSafe's hosted classifier.
{:ok, pipeline} =
ticket
|> Pipeline.extract(:last_message)
|> Pipeline.classify(
model: GenAI.Provider.Jev.Models.latest(),
questions: [%Question{id: "route", choices: ["billing", "technical"]}]
)
|> Pipeline.append_result(final: :json)
# The result is a correlated tool exchange plus a JSON assistant message.
{:ok, {streams, fork}} =
GenAI.stream(updated_fork, nil, stream_sink: self())
# PID sinks receive {:genai, {:branch, path, event}}.
# registry is your configured GenAI.Tool.Registry.
{:ok, {tools, fork}} =
GenAI.run_with_tools(fork, registry, nil,
max_iterations: 4, max_tool_calls: 8, max_concurrency: 2)
{:ok, %{completion: completion, summary: summary}} =
GenAI.Thread.Fork.Result.fetch(tools, [0])
# Partial failures return Fork.Error with outcomes and the updated fork.
Use Pipeline.aggregate to collect conversation payloads as lists, text, JSON or YAML; use structured extraction to prepare payloads and grading criteria. Joins combine, append or select branches explicitly. GenAI.run performs one inference; Pipeline.next_turn appends the assistant response. See the fork guide and classifier pipeline guide.
Get started with the latest source
The latest published package is GenAI v0.5.0: {:genai, "~> 0.5.0"} (GenAI Core 0.4.0 is on Hex and installs transitively). The fork, classifier and native DashScope examples on this page are part of the published release; the pinned Git source below remains the source-install alternative.
defp deps do
[
{:genai_core,
git: "https://github.com/noizu-labs-ml/genai-core.git",
ref: "94a9263b6eeeecaee2d93bbd5d9f1ea9440afa17", override: true},
{:genai,
git: "https://github.com/noizu-labs-ml/genai.git",
ref: "ced934a41781e5306132ee84a4a3cb7272a8b281"}
]
endconfig :genai, :anthropic,
api_key: System.get_env("ANTHROPIC_API_KEY")
config :genai, :qwen,
api_key: System.get_env("QWEN_API_KEY"),
native_base_url: System.get_env("QWEN_NATIVE_BASE_URL")
config :genai, :jev,
api_key: System.get_env("JEV_API_KEY")Native DashScope needs the native /api/v1 base for your workspace and region; OpenAI-compatible generation and System One classification use separate endpoints. Alibaba classification needs QWEN_DECISION_BASE_URL. Local Jev-Omni additionally needs ExLLama, a compatible GGUF and its exported head. If GenAI and ExLLama are sibling dependencies, keep the direct Core override above. Classifier setup and limits.