> LlamaIndex in 2026: an MIT-licensed Python data and agent framework with 300+ integrations, event-driven Workflows, and a company that has moved its focus to LlamaParse.
>
> Web page: https://balazscsorba.com/blog/llamaindex · Language: English · Also available in: [Deutsch](https://balazscsorba.com/de/blog/llamaindex.md) · [Magyar](https://balazscsorba.com/hu/blog/llamaindex.md)
> Author: Balázs Csorba · Published: 2026-07-14 · Keywords: llamaindex, llamaindex vs langgraph, llamaindex workflows, llama index agent framework, llamaparse pricing, rag framework python, durable workflows python, document agent framework

[Blog](https://balazscsorba.com/blog)/AI agents

# LlamaIndex review: the widest data toolkit, with its centre of gravity already moved

LlamaIndex in 2026: an MIT-licensed Python data and agent framework with 300+ integrations, event-driven Workflows, and a company that has moved its focus to LlamaParse.

[Balázs Csorba](https://balazscsorba.com/about)·July 14, 2026·10 min read

-   Agent framework
-   RAG
-   Python
-   Document parsing
-   Workflows

![Eight spokes around a central square hub, one much longer spoke reaching a distant box.](https://balazscsorba.com/images/blog/llamaindex/cover.webp?v=222a9c2759)

## Key takeaways

-   LlamaIndex is still the widest open-source data toolkit for LLM applications: more than 300 integration packages, one thin adapter each, on top of a small set of core abstractions.
-   Workflows, its orchestration layer, is event-driven: a step takes an event and returns an event, the graph is derived from type annotations, and it is validated before the run starts.
-   Durability is opt-in and hand-written. A run is ephemeral by default; snapshots come from \`Context.to\_dict()\` plus a loop the team writes, and resume is at-least-once, so steps must be safe to repeat.
-   The company's own README now states that the focus is document parsing and extraction. LlamaParse is the paid product, priced in credits, and LiteParse is the local open-source parser.
-   The verdict: use the open-source core, and specifically Workflows as a small library, but keep the framework's blast radius small, because the maintenance direction has plainly moved.

On this page

1.  [What it is](https://balazscsorba.com/#what-it-is)
2.  [How Workflows works](https://balazscsorba.com/#how-it-works)
3.  [Getting started](https://balazscsorba.com/#getting-started)
4.  [Durability and retries](https://balazscsorba.com/#durability)
5.  [Where it falls short](https://balazscsorba.com/#where-it-falls-short)
6.  [The pivot to document processing](https://balazscsorba.com/#the-pivot)
7.  [Verdict](https://balazscsorba.com/#verdict)
8.  [Sources](https://balazscsorba.com/#sources)

Listen to this article

0:000:00

LlamaIndex is an MIT-licensed Python framework for wiring LLM applications to data. It began as a set of connectors and index abstractions for retrieval, and it is still the widest such toolkit: the repository publishes more than 300 integration packages covering model providers, embedding models, vector stores, document loaders and rerankers. That breadth is why most teams meet the project first, and why it stays useful even though the company's own attention has moved elsewhere. The position taken here is simple: use the open-source core, and be deliberate about which hosted parts get adopted.

It sits between the model provider and the application. Nothing in the core talks to a model provider directly; every model, embedder and vector store arrives through an integration package implementing a core abstract class. That is a clean boundary and also the source of the main complaint: the same abstraction has to cover OpenAI, a local Ollama process and a dozen embedding models, so the useful common denominator is narrower than the surface area suggests. Against LangGraph it competes for the same orchestration role, against LangChain it competes on data handling, and against a hand-assembled vector store plus reranker it competes on convenience rather than capability.

## What it is

Package layout is the first thing to check, and it is where most confusion starts. `llama-index-core` holds the abstractions and ships Workflows with them. `llama-index` is the starter package that installs core plus a selection of integrations. Workflows also publishes on its own as `llama-index-workflows`, and when it arrives through core it is imported as `llama_index.core.workflow`. Everything else is a separate install, which is the only reason the dependency tree stays workable.

-   MIT licence. The current `llama-index-core` release is 0.14.25, published on 21 September 2026.
-   More than 300 integration packages on PyPI, each one a thin adapter over a core abstract class.
-   Workflows, the orchestration layer, is event-driven: a step receives an event and returns another, and the framework routes by type annotation rather than by a declared edge.
-   The event graph is validated before a run starts. Workflows where an event has no producer, a produced event has no consumer, or no terminal event is reachable are rejected.
-   Runs are ephemeral by default. Persistence is opt-in through `Context.to_dict()` snapshots, or through a runtime plugin such as DBOS that journals step transitions into a database.
-   Python is the practical target for the framework. Only LiteParse, the company's new local parser, ships bindings beyond Python.

## How Workflows works

Workflows is the part worth understanding, because it is also the part that outlives the framework's RAG reputation. A workflow is a subclass of `Workflow` whose methods are decorated with `@step`. Each step accepts one event type and returns another; returning the start event's type begins a run, returning `StopEvent` ends it. Branches are ordinary `if` statements that return different event types, and a loop is a step that returns an event type handled earlier in the graph. Concurrency is a step that returns `list[Event]`, paired with another step that accepts `list[Event]` and acts as the fan-in.

![Workflows: typed events move the run forward A start event triggers a retrieval step that fans out over eight parallel workers, a reranking step, and a terminal stop event. Below, a checkpoint step serialises the context to JSON after each finished step, and a resume path replays only the unfinished work.](https://balazscsorba.com/images/blog/llamaindex/diagram-7.webp?v=ef4c7d8e92)

The graph is derived from the step signatures and validated before the first event is dispatched. Persistence is not part of the runtime: it is a loop the team writes around it.

-   Return `list[Event]` when a step has a finite batch and can produce every work item before downstream workers start.
-   Accept `list[Event]` when the step needs the whole batch of results before it can continue.
-   Call `ctx.send_event(...)` when the number of events is unknown in advance, or when an event has to be dispatched from outside a step.
-   Use `ctx.store` for shared per-run state, and `Resource(...)` for clients, models and configuration that must not be serialised into a snapshot.

The type annotations are load-bearing rather than decorative. Before execution the framework derives the event graph from the step signatures and refuses to start a workflow with an unproduced event, an unconsumed event or no reachable `StopEvent`. That catches a class of wiring bug that a hand-written function composition does not catch at all, and it is the strongest argument for the design. The price is that a deliberately dynamic workflow has to fall back on `ctx.send_event` and declare which static checks to skip, which in practice means a workflow becomes progressively less checkable as it becomes more capable.

## Getting started

The smallest useful workflow is about twenty lines. Two install shapes are supported and they differ in the import path, which is the detail that catches people:

```
import asyncio

from llama_index.core import VectorStoreIndex
from workflows import Workflow, step
from workflows.events import Event, StartEvent, StopEvent

class Answered(Event):
    question: str
    answer: str

class TriageFlow(Workflow):
    index: VectorStoreIndex

    @step
    async def retrieve(self, ev: StartEvent) -> Answered:
        retriever = self.index.as_retriever(similarity_top_k=8)
        nodes = await retriever.aretrieve(ev.question)
        best = max(nodes, key=lambda n: n.score or 0.0)
        return Answered(question=ev.question, answer=best.node.get_content())

    @step
    async def answer(self, ev: Answered) -> StopEvent:
        return StopEvent(result=ev.answer)

async def main():
    flow = TriageFlow(index=VectorStoreIndex.from_documents(documents), timeout=60)
    result = await flow.run(question="What is the refund window?")
    print(result.result)

asyncio.run(main())
```

`run()` returns a `WorkflowHandler`. It is awaitable, and keeping the handler instead of awaiting it inline also gives access to `stream_events()` for progress reporting. The `timeout` argument is in seconds and is set on the constructor. Every step is async by design, so a standalone script needs a single `asyncio.run` entry point; inside FastAPI or a notebook it is not necessary.

**Choose the install shape deliberately**

If only the orchestration is needed, install `llama-index-workflows` alone; install `llama-index-core` when the retrieval components come with it, because that package re-exports Workflows under `llama_index.core.workflow`. The starter package `llama-index` pulls in a default selection of integrations, which most applications replace on the first day anyway.

## Durability and retries

Workflows are ephemeral by default: once `run()` returns, the state is gone and the next run starts from nothing. For a fan-out over hundreds of documents that should not restart from zero, the documented mechanism is a checkpoint loop. `Context.to_dict()` serialises the in-flight events and the state store, `Context.from_dict()` rebuilds them, and `run(ctx=...)` continues in a different process. There is no built-in checkpointer to switch on: the run emits an internal `StepStateChanged` event when a step finishes, and that is the signal to snapshot.

```
import json

from workflows.events import StepState, StepStateChanged

handler = flow.run(question=q)

async for ev in handler.stream_events(expose_internal=True):
    if isinstance(ev, StepStateChanged) and ev.step_state == StepState.NOT_RUNNING:
        json.dump(handler.ctx.to_dict(), open("run.json", "w"))

result = await handler

# after a restart: resume from the last snapshot
ctx = Context.from_dict(flow, json.load(open("run.json")))
result = await flow.run(ctx=ctx)
```

Two properties of this design surface in operations. Resume is at-least-once: a step that was mid-execution when the snapshot was taken is rewound and runs again, so up to `num_workers` items are duplicated. And the snapshot is JSON, so a value the serialiser cannot encode makes `to_dict()` raise and the whole snapshot fail, not just the offending field. Heavy inputs therefore belong in a `Resource`, which is re-created on resume instead of being serialised. Teams that would rather not own the loop can use the DBOS runtime plugin, which journals step transitions into a database and needs no checkpoint code.

## Where it falls short

The weaknesses are structural rather than bugs. Breadth is a maintenance surface: with hundreds of integrations, an upgrade can move the model wrapper you did not mean to touch, and pinning one integration often means pinning core. The high-level API hides enough that a prototype can reach production without anyone recording which chunk size, which top-k and which prompt template produced the answer; the defaults are convenient and are not documented as defaults. Durability is opt-in and hand-written, which is the opposite of what a long-running batch job wants. And the company behind the framework has publicly narrowed its focus, which is awkward to evaluate in 2026.

Framework

Orchestration model

Durability and state

Where it wins

**LlamaIndex Workflows**

Event-driven steps, control flow in plain Python

No built-in checkpointer; context snapshots written by the team, or the DBOS runtime plugin

Retrieval and document components live in the same library

**LangGraph**

Explicit state graph with conditional edges

Durable execution and checkpointers are the default

State transitions are the artefact, and stay inspectable as a graph

**Haystack**

Pipelines of typed components

Per-component error branches and retries

A stable component catalogue for retrieval-heavy applications

**DSPy**

Declarative modules, optimised offline against a metric

None; it is not an orchestration runtime

Prompt and module tuning, before any serving code exists

Read the durability column across and the practical split is legible. LangGraph makes checkpointing the default and buys that by making the graph explicit, which costs cognition and pays in legibility. Workflows keeps control in plain Python, which reads better and inspects worse: a workflow that fans out over 500 documents is 500 concurrent calls whose ordering no tool is helping you see. For a team whose work is to reason about state transitions, that is the wrong trade. For a team that wants to write ordinary Python and have it behave, it is the right one.

A second caveat belongs here. A dynamic workflow loses static analysis, and the documentation is explicit that unreachable steps and one-off events are exactly what those checks cannot see. Migrating a hand-drawn graph into Workflows tends to reach for `skip_graph_checks` early, which quietly disables the check that catches dead branches, which is the same check that would have caught the wiring mistake in the first place.

## The pivot to document processing

The repository README now carries an unmissable note: the current focus of LlamaIndex is document parsing and extraction, and LlamaParse is the company's enterprise platform for it. That changes what a maintenance budget is funding. The integration packages still ship and still work; what is no longer guaranteed is that each of them is extended when a new provider appears.

-   LlamaParse is the paid product: agentic OCR, parsing, extraction and indexing, sold in credits rather than in seats.
-   LiteParse is the open-source counterweight: a Rust parser that runs locally with no LLM, no cloud dependency and no API key, with bindings for TypeScript, Python, Rust and browser WASM.
-   ParseBench and ExtractBench are the company's own public benchmarks for parsing and extraction.
-   The framework stays MIT-licensed and published on PyPI, with `llama-index-core` at 0.14.25 as of 21 September 2026.
-   The result is a split strategy: open tooling for local parsing, a hosted platform for hard documents, and the agent framework in between as the integration surface.

That is a defensible business decision and a mild warning for anyone choosing a framework now. The parts of the stack most likely to be maintained are the parts behind the paywall, and the MIT core is what remains useful as a library. Read it as an argument for keeping the framework's blast radius small: use Workflows for orchestration, keep loading and parsing in house where that is possible, and be explicit about which calls leave your infrastructure.

**LlamaParse pricing, in numbers**

The pricing page sells credits, not seats: 1,000 credits at $1.25, 10,000 credits on the free tier, 40,000 on the $50 starter tier and 400,000 on the $500 pro tier, with pay-as-you-go caps of $500 and $5,000 a month. Concurrent parse jobs are 5 on free and starter, 20 on pro and 100 on enterprise. Parse, extract, classify and index all draw the same balance, and the same table lists smart result caching with a re-parse at zero credits, which is worth confirming for a specific parse configuration rather than assuming.

## Verdict

The verdict is that LlamaIndex remains the most complete answer to a narrow question, how to get documents into an LLM application without writing the integration layer yourself, and a mediocre answer to the broader question of how to orchestrate an agent. Workflows is genuinely good at turning branching logic into typed, validated Python, and its at-least-once checkpoint model is honest about its own failure semantics. What the framework does not offer is a runtime to point at and watch: no state graph to inspect, no persistence on by default, and a maintenance direction that has plainly moved elsewhere.

1.  Use it when most of your data is documents and the connector, chunker, embedder and reranker abstractions are already written for you. That is a real saving, and it was the original purpose.
2.  Use Workflows on its own as a small library when the orchestration is branching, looping Python currently tangled inside asyncio. The typed event graph and the pre-run validation are the reason.
3.  Do not pick it for a graph that has to be reasoned about. If the state transitions are the artefact under scrutiny, an explicit graph beats Python control flow.
4.  Do not pick it for the hosted platform. LlamaParse, Extract and the index service are a separate paid product with credit pricing, and tying your ingestion bill to a framework is a decision rather than a default.
5.  Avoid it for a TypeScript or Go stack. The core is Python; only the new LiteParse parser ships broader bindings.
6.  Re-evaluate in a year if the integration breadth is load-bearing. With the company's focus on parsing and extraction, the long tail of vector store and embedder integrations is the part most exposed to slow maintenance.

> The current focus of LlamaIndex is to build the best AI-powered engine for document parsing and extraction.

That sentence sits in the project README rather than in a blog post, which is unusual and worth noticing: the framework's own maintainers are the ones stating where the roadmap is going. Read as engineering, it says the orchestration layer is maintained but is not the destination, and the paid document pipeline is.

## Sources

1.  [LlamaIndex repository README and focus note](https://github.com/run-llama/llama_index)
2.  [Agent Workflows: introduction](https://developers.llamaindex.ai/python/llamaagents/workflows/)
3.  [Agent Workflows: writing durable workflows](https://developers.llamaindex.ai/python/llamaagents/workflows/durable_workflows/)
4.  [Agent Workflows: observability](https://developers.llamaindex.ai/python/llamaagents/workflows/observability/)
5.  [LlamaAgents overview](https://developers.llamaindex.ai/python/llamaagents/overview/)
6.  [llama-index-core on PyPI](https://pypi.org/project/llama-index-core/)
7.  [LlamaIndex and LlamaParse pricing](https://www.llamaindex.ai/pricing)
8.  [LiteParse documentation](https://developers.llamaindex.ai/liteparse/)
9.  [LangGraph on GitHub](https://github.com/langchain-ai/langgraph)

## Frequently asked questions

Is LlamaIndex still worth using in 2026?

Yes, with a narrowed scope. The MIT-licensed core is maintained, \`llama-index-core\` was at version 0.14.25 on 21 September 2026, and the Workflows orchestration layer is genuinely well designed for branching and looping logic. What has changed is the company's stated focus, which the README puts on document parsing and extraction rather than on the framework.

What are LlamaIndex Workflows and how do they work?

A workflow is a subclass of \`Workflow\` with methods decorated by \`@step\`. Each step accepts one event type and returns another, and the framework routes by type annotation, so branches are ordinary \`if\` statements and loops are steps returning an event handled earlier. Concurrency is a step returning \`list\[Event\]\` paired with a step accepting \`list\[Event\]\`. The event graph is validated before the run starts.

How does LlamaIndex handle durability and retries?

It does not, by default. Once \`run()\` returns, the state is gone. The documented mechanism is a checkpoint loop: serialise the context with \`Context.to\_dict()\` when a \`StepStateChanged\` event marks a step as finished, then rebuild it with \`Context.from\_dict()\` and call \`run(ctx=...)\`. Resume is at-least-once, so a step in flight when the snapshot was taken runs again. The DBOS runtime plugin is the alternative that removes the loop.

How much does LlamaParse cost?

The pricing page sells credits rather than seats: 1,000 credits at $1.25, 10,000 credits free, 40,000 on the $50 starter tier and 400,000 on the $500 pro tier, with pay-as-you-go caps of $500 and $5,000 a month. Concurrent parse jobs are 5 on free and starter, 20 on pro and 100 on enterprise. Parse, extract, classify and index all draw on the same balance.

Written by Balázs Csorba

Senior fullstack & AI engineer in Styria, Austria – 10+ years of Vue, Nuxt, Node.js and PHP, now building tooling for AI agents.

[AI engineering & MCP servers →](https://balazscsorba.com/expertise/ai-engineer)[About me →](https://balazscsorba.com/about)

## More articles

-   [Designing tools for AI agents: names, descriptions, schemas and error messages](https://balazscsorba.com/blog/agent-tool-design-descriptions-errors)
-   [Migrating a legacy codebase with coding agents: a strangler fig playbook](https://balazscsorba.com/blog/coding-agents-legacy-codebase-migration)
-   [Background coding agents that open PRs: a practical async workflow](https://balazscsorba.com/blog/background-agents-async-pr-workflow)
-   [Context rot in long-running agents: clearing, compaction, memory and sub-agents](https://balazscsorba.com/blog/context-rot-compaction-long-running-agents)

## Sounds like what you need?

Tell me about your project or role – I’d love to hear from you.

[Get in touch](mailto:contact@balazscsorba.com) [Connect on LinkedIn](https://www.linkedin.com/in/balazs-csorba)
