Latest release 2026.07.07 restructures the local AI subsystem from the ground up and ships one change every existing user needs to know about before upgrading. The full release write-up is on docwire.io; this post focuses on what changes in your code.
Breaking change first: everything is snake_case now
We converted all public type names to snake_case per our newly published coding guidelines. If you're upgrading from an earlier version:
- ChainElement
+ chain_element
- ParsingChain
+ parsing_chain
- PlainTextExporter
+ plain_text_exporter
- HtmlWriter
+ html_writer
- TransformerFunc
+ transformer_func
- FileStream
+ file_stream
- ZipReader
+ zip_reader
Document element tags and enums follow the same rule:
- document::Text
+ document::text
- mail::Mail
+ mail::mail
- openai::Model
+ openai::model
The migration is mechanical for the most part — nothing here compiles silently, so the compiler walks you through it. We chose one big cut over years of deprecation aliases: the codebase now matches the written standard it claims to follow.
Abstract AI runner interface
New pure virtual base class docwire::ai::ai_runner defines the contract for every local inference backend: process() for text generation tasks, embed() for embeddings, and unload() for deterministic teardown. Backends guarantee thread safety.
If you want to plug your own inference engine into a DocWire pipeline, this is the seam.
llama.cpp backend + IBM Granite
The new optional docwire_ai_llama library wraps llama.cpp behind the runner interface, so any GGUF model can drive summarization, translation, or embedding steps inside a pipeline — no network calls, no external services.
The local-ai-model-granite vcpkg feature installs IBM Granite 4.0 1B Q8_0 as the default model, so a working offline LLM pipeline is available out of the box.
Llama parameters are passed through dedicated strong types defined in model_inference_config.h — context_size, thread_count, token_limit, temperature, min_p, batch_size — instead of a bag of ints, so misordered arguments become compile errors.
Modular backends
The former monolithic local AI library is split:
| Library | Contents |
|---|---|
docwire_ai |
shared chain elements: ai::task, ai::summarize, ai::translate, ai::embed
|
docwire_ai_ct2 |
CTranslate2 runner and tokenizer (docwire::ai::ct2) |
docwire_ai_llama |
llama.cpp runner (docwire::ai::llama) |
Build only the backend you deploy:
# CMake options
-DDOCWIRE_CT2=ON
-DDOCWIRE_LLAMA=ON
# vcpkg features
local-ai-ct2
local-ai-llama
local-ai-model-granite
For the common path, the ai::local namespace adds convenience classes (summarize, translate, task, passage::embedder, query::embedder) that pick a sensible default backend and model, so a pipeline like parse → extract text → summarize locally stays a few lines.
EML parser: crash fixed, corner cases covered
Malformed MIME boundaries — prematurely closed multipart sections — could crash the underlying mailio library. The EML parser's BoundaryTracker now detects these and injects empty parts where needed. Four new test fixtures lock the behavior in:
endboundary_first.eml
missing_inner_closing_boundary.eml
nested_multiparts_missing.eml
valid_format.eml
Real mailbox data is dirty; parsers that fall over on it are not infrastructure.
Testing changes worth noting
- New end-to-end integration tests for both backends: full pipeline from document parsing through text extraction to a local AI task
- Local AI summarization tests moved to fuzzy matching — exact-string assertions against LLM output were a flakiness generator
- Example tests requiring local AI libraries compile conditionally on target availability
Links
- Full release notes
- Release blog post
- DocWire Manifesto and Coding Guidelines — new in this release
*Yes - we drafted it with AI assistance (the most stubborn one), then reviewed and edited ourselves — every technical claim above maps to the actual release. If we got something wrong anyway, tell us in the comments or in dm's *
Top comments (0)