LlamaIndex pushed llama-index-core v0.14.23 on June 24, 2026 — a point release that quietly retires one query engine and closes a state-corruption bug that has been biting long-lived services. Here is whether it earns a slot in your next deploy.
Is LlamaIndex 0.14.23 a Skip or a Deploy?
Deploy it — the upgrade is low-risk. LlamaIndex 0.14.23 is an incremental point release on the 0.14.x line with no announced breaking changes in core, so a pip install --upgrade llama-index is a small, additive step rather than a migration . The core changelog lists 17 entries, a mix of new multimodal features and bug fixes, and there is no separate prose release note explaining design intent — the CHANGELOG and the release commit are the primary record .
Quick Answer: LlamaIndex 0.14.23 (June 24, 2026) is a low-risk point release with no breaking core API changes. Upgrade it: you get first-class multimodal RAG and a fix for cross-request workflow state bleed. One thing to act on — SimpleMultiModalQueryEngine is now deprecated.
Two items deserve your attention before you ship. First, there is exactly one deprecation to act on: SimpleMultiModalQueryEngine now emits a deprecation warning — not an error — directing you to the standard engines, with removal expected on the 0.15.x line . Existing code keeps running on 0.14.23, so you can migrate on your own schedule, but treat it as a clock that has started.
Second, and easy to overlook: PR #21780 deep-copies a workflow's initial_state so state mutations no longer leak across separate runs . If you run a long-lived service that reuses a single Workflow instance across many requests, this silently fixes cross-request state bleed — the kind of bug that surfaces as intermittent, hard-to-reproduce wrong answers under concurrency. That fix alone justifies the bump for production workflow services.
The version mechanics are unremarkable, which is the point. The umbrella llama-index package moved from 0.14.22 to 0.14.23, and its core pin shifted from llama-index-core>=0.14.22,<0.15.0 to >=0.14.23,<0.15.0 . Core still declares Python >=3.10,<4.0 under an MIT license, so nothing about your runtime constraints changes . The sections below walk through what to swap, what got absorbed, and what got repaired.
Retiring SimpleMultiModalQueryEngine: The Replacement Calling Convention

SimpleMultiModalQueryEngine is deprecated as of llama-index-core 0.14.23, and the deprecation message names its own replacement: route multimodal queries through RetrieverQueryEngine.from_args(retriever=..., llm=..., multimodal=True) instead . For most callers this is a 3–4 line swap. The non-obvious work is the template migration, which moved onto the synthesizer.
Quick Answer: Replace SimpleMultiModalQueryEngine with RetrieverQueryEngine.from_args(retriever=..., llm=..., multimodal=True) in llama-index-core 0.14.23. Pass the multimodal model via llm= (not multi_modal_llm=), and convert separate text/image QA templates into chat-content templates on the synthesizer.
Three things changed in the calling convention. First, multi_modal_llm= is retired — you now pass the multimodal model through the standard llm= argument, the same one every other query engine already uses. Second, the separate text_qa_template= and refine_template= arguments give way to chat_content_qa_template and chat_content_refine_template, which carry rich chat-content blocks rather than plain text strings . Third, those template parameters live on the response synthesizer now: RetrieverQueryEngine.from_args(...) exposes chat_content_qa_template, chat_content_refine_template, and multimodal: bool = False, and the underlying Refine synthesizer gained the matching initialization paths .
There is a second exit path. If you relied on the citation behavior that SimpleMultiModalQueryEngine never offered cleanly, use CitationQueryEngine.from_args(..., multimodal=True) instead — it gives you citation-aware retrieval over mixed text, document, and image content through the same convention . Pick RetrieverQueryEngine for a straight synthesis swap and CitationQueryEngine when answers need source attribution.
Old vs. new, side by side
# Before (deprecated in 0.14.23)
from llama_index.core.query_engine import SimpleMultiModalQueryEngine
engine = SimpleMultiModalQueryEngine(
retriever=retriever,
multi_modal_llm=mm_llm,
text_qa_template=text_qa_tmpl,
image_qa_template=image_qa_tmpl,
)
# After (0.14.23)
from llama_index.core.query_engine import RetrieverQueryEngine
engine = RetrieverQueryEngine.from_args(
retriever=retriever,
llm=mm_llm, # was multi_modal_llm=
multimodal=True,
chat_content_qa_template=chat_qa_tmpl, # replaces text/image QA templates
chat_content_refine_template=chat_refine_tmpl,
)
The engine import and the multimodal=True flag are the mechanical part. The migration that bites is the templates: there is no longer a text template and a separate image template to set. You collapse both into a single chat-content template that the synthesizer feeds with multimodal blocks pulled straight from retrieved nodes . If your old code constructed two distinct prompt strings, plan to rewrite them as one chat-content structure before the swap compiles cleanly.
| Old argument | New argument | Notes |
|---|---|---|
multi_modal_llm= | llm= | Multimodal model now passes through the standard LLM slot. |
text_qa_template= | chat_content_qa_template= | Lives on the synthesizer; takes chat-content blocks, not plain text. |
refine_template= | chat_content_refine_template= | Same synthesizer-level move for the refine pass. |
| (separate image template) | (folded into chat templates) | No standalone image QA template; merge into one chat-content template. |
SimpleMultiModalQueryEngine(...) | RetrieverQueryEngine.from_args(..., multimodal=True) | Or CitationQueryEngine.from_args(..., multimodal=True) for citations. |
The deprecation does not break you at 0.14.x — the old engine still runs while it carries the warning — but the message is explicit about the destination, so there is no guesswork about the supported path . Migrate now rather than waiting for the 0.15 line, where a deprecated engine is the obvious candidate for removal.
RetrieverQueryEngine Absorbs Rich-Media RAG
The reason that destination exists at all is the second installment of LlamaIndex's multimodal work, which lands in 0.14.23 and finishes folding rich-media retrieval into the standard query stack. Two pull requests do the work: feat(core): Multimodal synthesis part 2 (PR #21561) and Multimodal query engines (PR #21784), both shipped in core v0.14.23 on 2026-06-24 . They complete an integration that began in v0.14.22 with the original "Multimodal synthesis" change. The practical result: a retriever and a response synthesizer now handle images, scanned pages, and video alongside text, so multimodal RAG stops being a separate code path.
Mechanically, when you call RetrieverQueryEngine.from_args(retriever=..., llm=..., multimodal=True), the synthesizer is configured to consume multimodal content blocks directly from retrieved nodes . That flag is the whole interface change: with multimodal=False (the default) you get the familiar text behavior; flip it to True and image or video blocks attached to your nodes are passed to the model instead of being discarded or pre-flattened to captions. You no longer maintain a separate image index, and you no longer write text-extraction glue to turn a retrieved figure into a string before synthesis.
The Refine response synthesizer gets the matching treatment. It gains chat_content_qa_template and chat_content_refine_template initialization paths, plus its own multimodal flag . This matters specifically for pipelines that refine an answer across several retrieved chunks rather than stuffing everything into one prompt — a common shape when context windows are tight or when you want incremental grounding. With the new templates, each refinement step can carry mixed content (a paragraph here, a chart there) instead of forcing every chunk into plain text first.
The scope is concrete. PDFs, scanned documents, and video content now flow through the same retriever-to-synthesizer chain as plain text chunks . A pipeline ingesting a directory of scanned contracts, or one indexing video transcripts with frame references, uses the ordinary engine you already know — only the multimodal=True switch differs.
The honest annotation: this is a convergence of two prior pathways, not a new primitive. Behavior is broadly equivalent to what the old SimpleMultiModalQueryEngine did — you can still retrieve over images and get a synthesized answer. What changed is the surface area. There is one engine type to learn, one synthesizer family to configure, and one code path for the maintainers to keep correct, instead of a general engine plus a special-case multimodal sibling. LlamaIndex frames this consolidation as part of moving past a narrow RAG identity toward a unified retrieval-and-synthesis core, arguing in its own writing that "LlamaIndex is more than a RAG framework" and that mixed-media retrieval belongs in the standard stack rather than a side door — the LlamaIndex team, LlamaIndex blog.
For a developer evaluating the upgrade, the value is not a capability you could not previously reach; it is a smaller, more durable API. Code written against RetrieverQueryEngine with multimodal=True sits on the path the project intends to keep supporting, which is the same reason the previous section pushed migration now rather than at 0.15 .
What 0.14.23 Repaired in Agentic Pipelines

For agentic pipelines, 0.14.23 is a correctness release, not a new abstraction. The four changes that matter all fix ways multimodal content or shared state could silently degrade as it moved through tools, memory, and reused workflow instances. There is no new top-level agent primitive here — FunctionAgent, function tools, and AgentWorkflow(agents=[...]) orchestration stay where they were . What changes is that the existing primitives stop dropping data.
The most consequential fix for production services is the workflow initial_state deep-copy in PR #21780. Before this, reusing one AgentWorkflow instance across requests meant state mutations from run N could bleed into run N+1 — silent cross-request data corruption that only surfaces under concurrency or long uptime, exactly where it is hardest to debug. The release now deep-copies initial_state so each run starts clean . If you instantiate a workflow once at boot and serve many requests off it — the normal pattern for a long-lived service — this is the line to read the changelog for.
The other three address multimodal context survival across the agent loop:
- Tool boundaries (PR #21678):
FunctionTool._parse_tool_outputnow handlesDocumentBlockandVideoBlock. Previously, rich content returned by a tool was flattened to text at the tool/agent boundary; now document and video blocks pass through intact . - Memory (PR #21728): URL-backed video and document blocks are now preserved across turns. Multimodal context that survived retrieval used to be discarded by memory; it now persists, so a follow-up question can still see the image or PDF the agent grounded its earlier answer in .
- Testing (PR #21732): a tool-calling mock LLM was added, enabling deterministic tests of agentic tool loops without a live model call .
Read together, the first two close a loop: rich-media RAG can now route content through tools and across memory without a lossy text conversion at either hop, matching the multimodal ChatMessage blocks the agent layer already expects . The mock LLM is the quieter win. Agent test suites have long been forced to choose between mocking at the HTTP layer or paying for live calls in CI; a first-class tool-calling mock lets you assert which tool the agent selected and how it parsed the result deterministically. If you build on top of LlamaIndex agents, it is worth restructuring tests around it.
None of these are headline features, and the generated changelog does not rank them. But for anyone running agents in production rather than a notebook, the initial_state fix alone justifies pulling the bump forward.
Ingestion Dedup and NE/NIN Operators: Quiet but Consequential

Below the multimodal headline, 0.14.23 ships a cluster of ingestion and retrieval fixes that change correctness and throughput without changing any API you call. Within-batch deduplication in the ingestion pipeline now uses a set instead of a list (PR #21755) , turning membership checks from O(n²) list scans into O(n) set lookups. On a handful of documents you will not notice; on batches of tens of thousands of nodes the difference is the gap between a quick run and a stalled one.
Quick Answer: LlamaIndex 0.14.23 (2026-06-24) replaces list-based ingestion dedup with a set for O(n) lookups (PR #21755), fixes pipeline.arun() RuntimeErrors inside async servers (PR #21765), and corrects NE/NIN metadata filters and TreeSelectLeafRetriever source attribution — all silent correctness wins that warrant re-running known queries after upgrading.
The async fix is the one most likely to have bitten you already. Async ingestion now detects the running event loop (PR #21765) , resolving a class of RuntimeError raised when pipeline.arun() was called inside FastAPI handlers or other async-native servers that already own the loop. If you previously wrapped ingestion in a thread to dodge that error, you can drop the workaround.
Two retrieval-correctness fixes deserve a verification pass rather than a glance. The NE and NIN metadata filter operators now return correct matches when metadata is missing (PR #21785) . If your vector search excludes documents by field inequality, run a known query before and after the upgrade and compare recall — results can legitimately shift. Separately, TreeSelectLeafRetriever now preserves source nodes (PR #21787) ; source attribution was silently absent in hierarchical retrieval, so any cite-source or grounding feature built on tree retrieval was returning answers without traceable sources.
| Component | Bug / change | PR | Who is at risk |
|---|---|---|---|
| Ingestion dedup | List → set for within-batch dedup; O(n²) → O(n) | #21755 | High-throughput ingestion of large batches |
| Async ingestion | Detects running event loop; fixes arun() RuntimeError | #21765 | FastAPI / async-native servers |
| NE / NIN filters | Correct matches on missing metadata | #21785 | Metadata-filtered vector search |
| TreeSelectLeafRetriever | Source nodes now preserved | #21787 | Cite-source / hierarchical retrieval |
| Token/Sentence splitters | RecursionError fixed when a unit exceeds chunk_size | #21900 | Pipelines chunking oversized tokens |
The last row closes a sharp edge: TokenTextSplitter and SentenceSplitter previously hit a RecursionError when a single unit exceeded chunk_size (PR #21900) , alongside a companion ZeroDivisionError guard in PromptHelper on empty input. None of these announce themselves in the generated changelog's flat list, but each maps to a failure mode you would otherwise debug in production.
Practical Checklist: Who Is Affected and What to Adjust
Whether 0.14.23 is a quiet point release or an urgent upgrade depends entirely on which of these five patterns your code matches. LlamaIndex shipped llama-index-core 0.14.23 on 2026-06-24 , and none of its consequences announce themselves. Run down the list, find the rows that describe your stack, and act on those before the changes compound into a production incident.
- You use
SimpleMultiModalQueryEngine: migrate now, not on removal day. The engine is deprecated at 0.14.23 and its message points you toRetrieverQueryEngine.from_args(retriever=..., llm=..., multimodal=True)orCitationQueryEngine.from_args(..., multimodal=True). The swap is three to four lines — pass the multimodal model viallm=instead ofmulti_modal_llm=and replace your split text/image QA templates with chat-content templates. Deferring it only trades a calm edit today for a scramble when the 0.15.x line drops the symbol. - You run a long-lived service that reuses an
AgentWorkflowacross requests: upgrade for theinitial_statedeep-copy fix (PR #21780), which stops state mutations from one run leaking into the next . This was silent per-request corruption, not a crash. Add a test that fires two runs against one shared workflow instance and asserts the second sees clean state. - You do metadata-filtered vector search with
NEorNIN: the missing-metadata behavior for those operators was corrected (PR #21785) . Re-run a known query after upgrading and verify recall. If results shift, the old answers were wrong, not the new ones — but you want to see that shift in staging, not from a user. - You run high-throughput batch ingestion: set-based within-batch dedup (PR #21755) and running-event-loop detection (PR #21765) are free performance and async-correctness wins . No code change required beyond bumping the version.
- You call Bedrock through Converse:
llama-index-llms-bedrock-converse0.14.14 fixes parsing of streamedtool_kwargsfrom string to dict and serializes rich content blocks in tool results . Malformed tool schemas on streamed calls were failing silently before; if your agents stream tool calls on Bedrock, treat this as the reason to upgrade.
The takeaway is narrow and concrete: 0.14.23 is a low-risk bump on the 0.14.x line with no breaking core changes , but two of its fixes — the workflow state isolation and the NE/NIN filter correction — were repairing silent wrong-answer bugs. If either row describes you, upgrade and verify deliberately rather than assuming a point release is safe to ignore.
Last updated: 2026-06-25. Based on the llama-index-core 0.14.23 changelog and release commit dated 2026-06-24.
Frequently asked questions
What replaces SimpleMultiModalQueryEngine in LlamaIndex 0.14.23?
Use the standard query engines with the new multimodal flag: RetrieverQueryEngine.from_args(retriever=..., llm=..., multimodal=True) or CitationQueryEngine.from_args(..., multimodal=True). Two argument changes come with the swap: pass the multimodal model via llm= instead of the old multi_modal_llm=, and replace the separate text/image QA and refine templates with the unified chat_content_qa_template and chat_content_refine_template. This is the exact migration path printed in the deprecation message at 0.14.23 LlamaIndex.
Will my existing multimodal code break immediately on upgrading to 0.14.23?
No. SimpleMultiModalQueryEngine is deprecated, not removed — instantiating it raises a warning, not an exception, and the class still executes as before. You have a migration window rather than a hard break. Removal is expected at the next minor line (0.15.x), so plan the swap to RetrieverQueryEngine or CitationQueryEngine before then rather than after an upgrade surprises you. The 0.14.23 release is a point release on the 0.14.x line with no announced breaking core changes (CHANGELOG.md).
What exactly was the initial_state workflow bug, and how do I know if I'm affected?
If your service instantiates one AgentWorkflow at startup and reuses that instance across many requests, mutations to the workflow's initial_state in one run could leak into subsequent runs. The fix in PR #21780 deep-copies initial_state per run so each request starts clean (release notes). The symptom to look for: user A's context appearing in user B's response in a long-lived stateful service. If you create a fresh workflow per request, you were not exposed; if you share one instance, upgrade and verify.
Does LlamaIndex 0.14.23 add new LLM providers?
It extends existing integrations rather than adding new providers. llama-index-llms-bedrock-converse (0.14.14) fixes parsing of streaming tool_kwargs from string to dict and serializes rich content blocks in tool results, while llama-index-llms-openai (0.7.9) adds support for the vLLM reasoning field (release notes). The changelog's model allowlist also lists Claude Opus 4.8 and Claude Fable 5, but these are LlamaIndex config entries — not independent general-availability announcements from the vendors, so treat them as lower-confidence.
Is llama-index-core 0.14.23 safe to pin in a production requirements file today?
Yes, with standard pre-production testing. llama-index-core ships under an MIT license, targets Python >=3.10,<4.0, and is an incremental point release with no announced core breaking changes (PyPI). The umbrella llama-index package moves its core pin from >=0.14.22,<0.15.0 to >=0.14.23,<0.15.0 (CHANGELOG.md). Run your own regression pass before deploying, given the NE/NIN filter-operator and workflow state-isolation correctness fixes this version carries.