Eino: a Go framework for LLM applications, split across two repositories
The ultimate LLM/AI application development framework in Go.
At a glance
- What is it?
- Eino gives Go teams typed component abstractions, a graph composition layer, and an agent kit. The core repository ships interfaces and orchestration; concrete model and store implementations live in a separate repo, and the current release line is an alpha.
- Who is it for?
- Adopt Eino if your application is already Go and you want typed component interfaces, a graph compiler, and an agent loop that share one runtime instead of gluing a Python framework to a Go service over HTTP. Do not adopt it if you need a stable tagged release today: the newest published versions are v0.10.0-alpha.31 and its alpha predecessors, so pin an exact version and read the changelog before any upgrade.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, 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 gap Eino fills for Go services that call models
Most LLM application tooling is written for Python or TypeScript. A Go backend that wants a retrieval step, a prompt template, a tool call and a streaming response has usually had two options: call out to a Python service, or hand-roll the glue. Eino is aimed at the second group. The README describes it as an LLM application development framework in Golang that draws from LangChain and Google ADK while following Golang conventions. That last clause is the design claim worth testing, because Go conventions mean explicit types, no dynamic dispatch by string, and errors returned rather than raised. The README's own example makes the shape concrete: openai.NewChatModel returns a model value, adk.NewChatModelAgent wraps it, and adk.NewRunner.Query returns an iterator you pull from with a for loop and an ok boolean. Nothing in that flow is reflection-driven. The audience is a team that already owns a Go service and wants the model orchestration to live inside it, not beside it.
Two repositories, and which one you actually import
The framework is deliberately split. This repository holds type definitions, the streaming mechanism, component abstractions, orchestration, agent implementations and the aspect mechanism. EinoExt holds component implementations, callback handlers, usage examples, evaluators and prompt optimizers. The practical consequence is that importing the core module gets you interfaces such as ChatModel, Tool, Retriever, Embedding and ChatTemplate, but not a working OpenAI client. The README points at eino-ext for official implementations covering OpenAI, Claude, Gemini, Ark, Ollama and Elasticsearch. So a first integration touches at least two modules, and the version relationship between them is something you have to check yourself; the supplied material does not state a compatibility matrix. There is also a third repository, eino-examples, referenced repeatedly for runnable code. If you are evaluating Eino, reading only the core repo will give you an incomplete picture of what a real application looks like.
Graphs as the composition primitive, with agents layered above
The mechanism the README shows in most detail is compose. You create a typed graph with compose.NewGraph, add nodes with AddLambdaNode, AddChatModelNode and similar calls, connect them with AddEdge, and call Compile to get a runnable. The example wires START to a validate lambda, then to a chat model node, then to a format lambda, then to END. Input and output types are generic parameters on the graph, so a mismatched edge is a compile error rather than a runtime surprise. Agents sit on top of the same substrate. adk.NewChatModelAgent takes a model and an optional ToolsConfig containing a compose.ToolsNodeConfig with a slice of tool.BaseTool values, and the README states the agent handles the ReAct loop internally, deciding when to call tools and when to respond. The two layers meet through graphtool.NewInvokableGraphTool, which wraps a compiled graph as a tool so an agent can invoke a deterministic pipeline. That is the interesting architectural bet: the same node and edge model serves both fixed workflows and autonomous loops, rather than the framework maintaining separate execution engines for each.
Getting a minimal agent running
The README's quick start is short enough to reproduce in full. You construct a chat model with openai.NewChatModel, passing a config struct with Model set to "gpt-4o" and APIKey read from os.Getenv("OPENAI_API_KEY"). You pass that model into adk.NewChatModelAgent with a ChatModelAgentConfig. You wrap the agent in adk.NewRunner with a RunnerConfig, call runner.Query with a prompt string, and loop over iter.Next() until it returns ok false, printing event.Message.Content. Adding tools means adding a ToolsConfig field whose ToolsNodeConfig holds a Tools slice of tool.BaseTool values. Two details are worth noting for anyone writing this from scratch. First, the examples discard errors with the blank identifier, which is fine for a README and wrong in production; every constructor here can fail. Second, the config keys are nested three deep by the time you reach Tools, so a helper function that builds a ToolsConfig once is worth writing early. The DeepAgent path uses a different constructor, deep.New with a deep.Config taking a ChatModel, a SubAgents slice of adk.Agent, and the same ToolsConfig shape.
Streaming is handled by the framework, not by each component
Streaming is the feature with the most design weight behind it. The README states that Eino automatically handles streaming throughout orchestration, concatenating, boxing, merging and copying streams as data flows between nodes, and that components implement only the streaming paradigms that make sense for them while the framework handles the rest. This matters because the alternative is that every component author reimplements the same buffering logic, and every graph author has to reason about where a stream becomes a value. Putting it in the runtime means a lambda node that knows nothing about streaming can sit between two nodes that do. The cost is that the rules are non-obvious. If you are debugging why a token arrives late or why a merge produced an unexpected ordering, the framework is doing work you did not write and cannot see from the node code. The README links a dedicated page on stream programming essentials, which is the document to read before you build anything where latency is visible to a user.
Callbacks, interrupt and resume, and what they cost you
Two cross-cutting features are described in the README with enough specificity to evaluate. Callback aspects inject logging, tracing and metrics at fixed points named OnStart, OnEnd, OnError, OnStartWithStreamInput and OnEndWithStreamOutput, applied across components, graphs and agents. Fixed hook points are easier to instrument than ad hoc logging, but they constrain you: if you need a metric that does not map onto one of those five moments, you are adding it inside a node. Interrupt and resume lets any agent or tool pause for human input and resume from a checkpoint, with the framework handling state persistence and routing. The README does not say where checkpoints are stored or how they are keyed, and that is the first thing to establish before designing around it, because the answer determines whether the feature is usable in a stateless deployment or requires a backing store you have to operate. The human-in-the-loop example directory is the place to look for the concrete shape.
The alpha release line is the main adoption risk
The recent release list shows v0.10.0-alpha.31, v0.10.0-alpha.30 and v0.10.0-alpha.29, published within days of each other in early September 2026. Three alpha releases in a week is a signal about the pace of change on that line, not about quality, but it has a direct operational consequence: an unpinned dependency can move under you between builds. If you adopt Eino, pin the exact version in go.mod and read the release notes before bumping, especially across alpha increments. The framework also has a structural limitation worth naming. It is Go-only by design, so any part of your stack that is Python or TypeScript stays outside it, and the boundary between them is HTTP or a queue that you write and maintain. And because the core repo ships abstractions while eino-ext ships implementations, a provider that has no implementation in eino-ext means writing a ChatModel or Retriever yourself against the interface. That is a real cost, and it is the case where Eino is the wrong tool for a small team with a short deadline.
Where Eino sits against LangChain and LangChainGo
The README names LangChain and Google ADK as influences, and the repository topics include langchain and langchain-for-go, so a comparison is fair. The difference in approach is in the type system. LangChain's Python chains are assembled from objects that are largely untyped at the seam, which makes rapid prototyping cheap and refactoring expensive. Eino's graph is parameterised on input and output types and compiled, so the wiring is checked before the program runs. LangChainGo occupies similar territory for Go, and the README's framing of Eino as following Golang conventions is effectively a claim about how it differs from a direct port. The honest read is that Eino's composition layer is the part that is hardest to replicate by hand, and the agent layer is the part most likely to change under you given the alpha cadence. If you only need a typed client for one provider, you do not need this framework at all. If you need a graph that mixes deterministic steps with model calls and want the compiler to catch your mistakes, that is the case Eino is built for.
Editorial conclusion
Adopt Eino if your application is already Go and you want typed component interfaces, a graph compiler, and an agent loop that share one runtime instead of gluing a Python framework to a Go service over HTTP. Do not adopt it if you need a stable tagged release today: the newest published versions are v0.10.0-alpha.31 and its alpha predecessors, so pin an exact version and read the changelog before any upgrade. Verify first that the provider you actually call has an implementation in eino-ext rather than only in the core abstractions, and read the callback manual before you wire tracing, because the hook points are fixed at OnStart, OnEnd, OnError, OnStartWithStreamInput and OnEndWithStreamOutput.
Community notes