brainlid/langchain: An Elixir LLM Framework That Refuses Parity With Python
Elixir implementation of a LangChain style framework that lets Elixir projects integrate with and leverage LLMs.
At a glance
- What is it?
- The Elixir LangChain port targets BEAM applications that need to talk to Claude, OpenAI, Gemini, or a self-hosted Bumblebee model. Its README states plainly that it does not aim for parity with the JS and Python libraries, and that choice shapes everything from the API to the upgrade path.
- Who is it for?
- Adopt brainlid/langchain if you are already running Elixir 1.17 or higher and want chat, tool, and provider abstractions that live in the same supervision tree as the rest of your app. Skip it if you need serialized prompt objects interchangeable with LangChain Python or JS, or if you depend on a vector store integration that the framework does not ship.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Elixir, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The Elixir gap this library fills
Elixir teams that want an LLM in a Phoenix app have three unappealing options: shell out to a Python service, hand-roll HTTP calls against each provider's REST API, or adopt a framework written for a different language. brainlid/langchain exists for the third case. The README frames the goal as letting Elixir applications integrate AI services and self-hosted models, and it lists the providers it speaks to: Anthropic Claude, AWS Bedrock Mantle, OpenAI's Chat Completions and Responses APIs, Cloudflare Workers AI, xAI Grok, Google Gemini, Google Vertex AI, DeepSeek, Ollama, Mistral, Perplexity, orq.ai, Bumblebee, and ReqLLM. That breadth is the pitch. A team that already runs Oban, Phoenix, and Ecto does not want a second runtime and a second deployment artifact just to call a chat completion endpoint. The library's stated value props split into two: components (abstractions for working with language models, usable whether or not you adopt the rest of the framework) and off-the-shelf chains (a structured assembly of components for specific higher-level tasks). The README is explicit that off-the-shelf chains make starting easy, while components make it possible to customize or replace them. That two-layer split is the same shape as the Python and JS projects, but the Elixir version is not trying to be a drop-in replacement for either.
Why the README rejects parity with LangChain Python and JS
The most interesting design decision in this repository is a refusal. The README states that the JavaScript and Python projects aim to integrate with each other as seamlessly as possible, to the point where prompts, LLMs, and chains are designed to be serialized and shared between those two languages. The Elixir version does not aim for that. Two reasons are given. First, JavaScript and Python are object-oriented while Elixir is functional, and the maintainer writes that the project will not force a design that does not apply. Second, the JS and Python versions started before conversational LLMs were standard and put effort into preserving conversation history when the model did not support it. The Elixir library does not do that. This is a real trade-off, not a marketing line. It means an Elixir service cannot hand a serialized chain to a Python worker and expect it to deserialize, and it means prompt templates authored for LangChain Python will need rewriting rather than importing. If your architecture assumes a polyglot LangChain estate, this library is the wrong tool, and the README says so before you install anything. If your architecture is Elixir-first, the dropped constraint buys a smaller surface that follows the language's idioms instead of emulating another language's object graph.
Installation, version pinning, and the Req dependency
The README requires Elixir 1.17 or higher. Installation is a single dependency line in mix.exs: {:langchain, "~> 0.9.0"}. Note that the README example pins the 0.9 series while the repository's recent releases list v0.13.1, v0.13.0, and v0.12.0, so the documentation snippet lags the published versions. Check Hex for the current version rather than copying the README value verbatim. The library is written to use the Req HTTP client for API calls, which means Req arrives as a transitive dependency and its configuration and telemetry apply to your LLM traffic. Configuration lives in config/runtime.exs. The README shows keys such as openai_key, openai_org_id, anthropic_key, and xai_api_key, each set either from System.fetch_env! or from a literal string. It also supports indirection: a tuple of {MyApp.Secrets, :openai_api_key, []} or a zero-arity function, which lets you resolve secrets at call time rather than boot time. For fly.io deployments the README gives the concrete commands: fly secrets set OPENAI_API_KEY=MyOpenAIApiKey, fly secrets set ANTHROPIC_API_KEY=MyAnthropicApiKey, fly secrets set XAI_API_KEY=MyXaiApiKey. The README's own warning is that API keys should be treated as secrets and not checked into your repository. Nothing here is unusual for an Elixir library, but the tuple and function forms matter in practice: they let you rotate a key without a redeploy if your secrets module reads from a runtime source.
Provider breadth and the OpenAI-compatible escape hatch
The supported chat models list is the most concrete part of the README and the easiest way to judge fit. Native modules exist for Anthropic Claude (including extended thinking support and AWS Bedrock), OpenAI ChatGPT via Chat Completions, OpenAI's newer Responses API with WebSocket transport support, xAI Grok (Grok-4, Grok-3-mini, Grok-4 Heavy multi-agent), Google Gemini, Google Vertex AI, DeepSeek with prompt caching support, Ollama, Mistral, Perplexity, orq.ai, and Bumblebee for self-hosted models via Nx covering Llama, Mistral, and Zephyr. Two entries deserve attention. AWS Bedrock Mantle is described as an OpenAI-compatible gateway for third-party models hosted on Bedrock, naming the Moonshot Kimi K2 family and OpenAI gpt-oss among others, and it is reached through ChatOpenAI. Cloudflare Workers AI works the same way, also via ChatOpenAI. That pattern is worth understanding before you write adapter code: when a provider exposes an OpenAI-compatible surface, this library may not need a dedicated module, and the README points you at the OpenAI path instead. The ReqLLM entry is a different kind of escape hatch, a multi-provider adapter via the req_llm library covering Anthropic, OpenAI, Gemini, Groq, Ollama, and AWS Bedrock. So there are two extension routes, OpenAI compatibility and ReqLLM, and the README does not rank them. If you are choosing between a native module and a compatibility shim, the README does not state which features are lost on the shim path, and that is a gap you would need to resolve from the Hex docs.
Prompt caching and where the documentation stops
The README's final visible section is titled Prompt caching and states that ChatGPT, Claude, and DeepSeek all offer prefix-based prompt caching, which can offer cost and performance benefits for longer prompts. The text is truncated at that point, so what can be confirmed is limited: the DeepSeek entry in the supported models list mentions prompt caching support, and the README groups the three providers under the same prefix-caching heading. Whether caching is opt-in per request, automatic per provider, or configured through a specific key is not visible in the supplied material. Treat that as an open question to answer from https://hexdocs.pm/langchain/ before designing around it. The same caution applies to the Bumblebee path. The README lists Bumblebee as self-hosted models via Nx supporting Llama, Mistral, and Zephyr, which tells you the integration exists but not what hardware, memory, or startup cost it implies. For an Elixir team, running a model in-process on the BEAM is a fundamentally different operational profile from calling an API, and the README does not discuss it. The documentation is honest about scope and thin on operational detail. That is a reasonable trade for a library at v0.13, but it means the Hex docs, not the README, are where you will spend your evaluation time.
Where this library is the wrong choice
Three cases stand out. The first is polyglot LangChain. If your team already has LangChain Python services and expects to share serialized prompts, LLMs, or chains across languages, the README's non-parity stance makes this library incompatible by design, and no amount of adapter code fixes an intentional divergence. The second is anything requiring a vector store or retrieval pipeline. The README's supported list is entirely chat and completion providers. There is no vector database integration named in the material, so retrieval-augmented generation would be assembled from separate Elixir libraries and wired in by hand. The third is version stability. The repository shows three releases inside roughly three weeks in August 2026 (v0.12.0, v0.13.0, v0.13.1), and the README's install snippet still points at the 0.9 series. Pre-1.0 libraries move; a team that cannot absorb breaking changes between minor versions should either pin hard or wait. There is also a licensing question. The repository metadata reports a license of NOASSERTION, which means the automated classifier could not map the license file to a known SPDX identifier. The README does not discuss licensing at all. Before adopting this in a commercial product, read the LICENSE file in the repository and have your own counsel determine the terms; this article cannot and does not give legal advice.
How it compares to calling provider APIs directly
The obvious alternative is skipping the framework and calling each provider's REST API from Elixir with Req, which the library already uses under the hood. The difference is what you write and maintain. Direct calls mean one HTTP client module per provider, your own request and response structs, your own handling of streaming, tool calls, and provider-specific error shapes, and your own retry logic. brainlid/langchain offers shared abstractions for working with language models plus implementations for each, and the README says components are modular and usable whether or not you adopt the rest of the framework. That last clause is the practical argument: you can take the Anthropic module and ignore the chain machinery. The cost is a dependency you do not fully control, on a pre-1.0 release cadence, with a provider matrix that shifts between versions. A second alternative is routing through ReqLLM, which the README lists as a supported adapter covering Anthropic, OpenAI, Gemini, Groq, Ollama, and AWS Bedrock. That inverts the dependency: ReqLLM owns provider diversity and this library becomes one consumer of it. The README does not compare the two paths or say which is preferred, so the choice comes down to whether you want provider modules maintained inside this repository or delegated to a separate library with its own release cycle.
Maintenance cost and the upgrade decision
Elixir 1.17 or higher is a hard floor, so an application on an older toolchain must upgrade the language before it can use the library at all. The release cadence visible in the repository is fast: v0.12.0, v0.13.0, and v0.13.1 within about a month, and the last push to main is dated after the newest release. For a pre-1.0 library, that pace usually means API adjustments arrive with minor version bumps, and the README's install snippet pointing at 0.9 while 0.13.1 is published is a small but telling signal that documentation trails code. Budget for reading changelogs on every upgrade rather than trusting the tilde requirement to keep you safe. On the positive side, the configuration surface is small and stable in shape: keys in config/runtime.exs, secret resolution through environment variables, tuples, or functions, and secrets set on the host with fly secrets set. That is cheap to maintain. The expensive part is provider drift. Models are added and retired by vendors on their own schedules, and the README's model list links out to Anthropic, OpenAI, xAI, and Gemini documentation rather than enumerating model identifiers, which pushes the responsibility for staying current onto you. If your application depends on a specific model version, verify it against the provider's own documentation before assuming the library exposes it.
Editorial conclusion
Adopt brainlid/langchain if you are already running Elixir 1.17 or higher and want chat, tool, and provider abstractions that live in the same supervision tree as the rest of your app. Skip it if you need serialized prompt objects interchangeable with LangChain Python or JS, or if you depend on a vector store integration that the framework does not ship. Before writing production code, pin a version in mix.exs, read the changelog between v0.12.0 and v0.13.1, and confirm your chosen provider module appears in the supported chat models list in the README.
Community notes