This document covers the major breaking upgrade paths.
Core 0.19.0 and llamadart_litert_lm_flutter 0.19.0 move to published
LiteRT-LM v0.19.1-1
(owner 82d4ae1c, upstream 7a03724c), which keeps the C API 1.0.0 exports of
v0.19.0, so the 0.18.0 ABI selection below
applies unchanged. Web LiteRT-LM moves to @litert-lm/core@0.19.0 in 0.19.0.
The public LiteRtLmRuntimeClient, default services and speech worker now share
lazy ABI-aware discovery. Runtimes without litert_lm_get_api_version use the
legacy client; a successful version probe reporting exactly 1.0.x selects the
C API 1 client. Failed probes, malformed versions and other versions throw
LlamaUnsupportedException before engine calls; they never retry legacy
signatures. Required exports and optional capability groups are checked before
use. A version marker alone does not establish feature support.
The selected native loader requires macOS 14 or newer on Apple silicon before loading core or companion libraries, including custom paths. Older or unverifiable macOS versions are refused even when the library would use the legacy ABI. Upgrade macOS or use llama.cpp. Other platform/backend restrictions remain in the support matrix.
Behavior to recheck with a C API 1 runtime:
- Initial text LoRA adapters remain unsupported by the advanced engine. Use a compatible llama.cpp model/adapter pair.
LiteRtLmRuntimeClient.initialize(maxNumImages: ...)is an image-history hint, not an enforced capacity. V1 accepts actual images dynamically and requires a configured vision encoder for that hint.- Thinking budgets, JSON Schema, media and dedicated ASR require their own admitted exports and model/configuration capabilities. Unsupported combinations throw typed errors; accepting the ABI does not qualify a model or device.
- A missing final callback, failed native drain or uncertain cleanup retains the worker and its native/model/cache owners. That backend requires a process restart; an ended request does not prove native cleanup completed.
See the adoption checklist for per-feature coverage.
The hook and the llama.cpp Apple companion source pin v0.8.0-1. Flutter iOS and
macOS apps get it with llamadart_llama_cpp_flutter 0.17.1, which requires
core 0.17.1. A locked companion 0.17.0 keeps v0.7.0-3 and builds with an
upgrade warning. Web moves to llama.cpp v0.8.0 too. The LiteRT-LM and
stable_diffusion companions also release at 0.17.1 with unchanged pins, and
their 0.17.0 releases keep working. No Dart API changes.
Behavior to recheck after upgrading:
- A hand-written
grammarnested or chained more than 256 levels deep throwsLlamaInferenceException. ModelSplitMode.rowon a native llama.cpp device without split buffers throwsLlamaUnsupportedExceptionbefore the load; useModelSplitMode.layer.GpuBackend.autoon macOS x86_64 runs on the CPU. ExplicitGpuBackend.metalstill uses Metal.- Opt-in
GpuBackend.blasat the default thread count uses at most 4 threads; set an explicit count to raise it. - LFM2 and LFM2.5 with
parallelToolCalls: truecan need a highermaxTokens: a call list cut off before it closes returns no calls.
Upgrade core and every Apple companion used by the app to 0.17.x together.
Companions now constrain core to '>=0.17.0 <0.18.0'. The llama.cpp and
stable_diffusion companions move to v0.7.0-3 and v0.3.0-1; LiteRT-LM keeps
v0.18.1-4.
Custom backends and backend test fakes only. LlamaBackend no longer
declares setLoraAdapter, removeLoraAdapter, clearLoraAdapters,
multimodalContextCreate, multimodalContextFree, supportsVision or
supportsAudio. They move to two optional interfaces in
package:llamadart/backend.dart:
BackendLoraSupport: the three LoRA members.BackendMultimodalSupport: the four projector members.
A backend that supports either implements it next to LlamaBackend
(implements LlamaBackend, BackendLoraSupport, BackendMultimodalSupport);
its @override annotations stay valid. A backend that only threw from
these members deletes them. On a backend without the interface,
LlamaEngine LoRA calls and projector loads throw
LlamaUnsupportedException naming the missing interface, before
setLoraSource, loadMultimodalProjectorSource or setModel downloads
anything; removeLoraSource of an adapter never applied does nothing, and
vision and audio input come from BackendDirectMediaInput alone. Code that
called these members through a LlamaBackend-typed value checks
backend is BackendLoraSupport or backend is BackendMultimodalSupport
first.
The built-in routers (LlamaBackend() on native and the Web) implement both.
When the selected runtime, such as LiteRT-LM, lacks one, they throw
LlamaUnsupportedException naming the runtime instead ("LoRA adapters are
not supported by the active Web runtime."). LiteRtLmBackend implements
neither.
ImageModelRole gains controlNet. A switch over ImageModelRole without
a default case no longer compiles; add the case or a default. A file whose
header shows ControlNet tensor names is now assigned this role instead of
failing classification: outside the bounded SD 1.x macOS CPU recipe,
ImageGenerationEngine.load throws LlamaUnsupportedException instead of
LlamaModelException. A ControlNet loaded with no file holding diffusion
weights (for example, alone) still throws LlamaModelException.
enableThinking, which defaults to true, now reaches every chat template as
enable_thinking, including responseFormat and tool-call requests, so
templates such as GLM-4.6, Apertus and DeepSeek-R1 distills render thinking
as llama-server does: Apertus now deliberates and DeepSeek-R1 distills open a
thought unless the request passes enableThinking: false. A boolean
enable_thinking or thinking in chatTemplateKwargs now overrides
enableThinking. With thinking off, <think> blocks that DeepSeek V3.1,
Qwen3, QwQ and Qwen3.5 write stay in the content instead of being dropped.
The hook and the llama.cpp Apple companion source pin v0.7.0-3. A Flutter
iOS or macOS build needs companion 0.17.0, which pins it; a companion
pinning v0.7.0-2 or earlier fails with Incompatible Apple llama.cpp companion. Apps that depend on a published companion hit pub resolution
first, because companions before 0.17.0 constrain llamadart below
0.17.0.
The hook and the stable_diffusion Apple companion source pin v0.3.0-1. A
Flutter iOS or macOS build needs companion 0.17.0, which pins it; a
companion pinning v0.3.0 fails with Incompatible Apple stable_diffusion companion, because that runtime aborts on FP8 weights on Metal, which this
core now loads there. Upgrade core and companion together.
Upgrade core and every Apple companion used by the app to 0.16.x together.
Companions now constrain core to '>=0.16.0 <0.17.0'. The llama.cpp companion
keeps v0.7.0-2; LiteRT-LM and stable_diffusion move to v0.18.1 and
v0.3.0.
A load throws LlamaUnsupportedException naming each ModelParams field set
away from its default that the model's runtime does not apply. Earlier
releases loaded the model and dropped these fields:
| Runtime | Newly rejected fields |
|---|---|
| llama.cpp, native and Web | liteRtLmBackend, liteRtLmActivationDataType, liteRtLmPrefillChunkSize, liteRtLmParallelFileSectionLoading, liteRtLmDispatchLibDir, liteRtLmCacheDir, liteRtLmMaxProgramCacheBytes |
| llama.cpp, native | preferMemory64, modelBytesHint |
| llama.cpp, WebGPU bridge | useMmap: false, useMlock: true |
| LiteRT-LM, native and Web | loadMtp, speculativeRollbackTokenMax, preferMemory64, modelBytesHint |
An app that shares one ModelParams across GGUF and .litertlm models, or
across native and Web, sets these fields only for the runtime that applies
them, for example with copyWith and the matching clear* flags.
LlamaEngine.load and setModel on LlamaBackend() reject them before any
download when ModelSource.format or the file name names the model format;
the deprecated loadModelSource and a model known only from its file header
reject them when the model loads, before the runtime starts.
LiteRT-LM models on macOS x86_64, Windows arm64 and the x86_64 iOS simulator,
which have no LiteRT-LM runtime, and an explicit device the platform's
LiteRT-LM lacks, such as gpu on Linux arm64, also fail before any download.
LlamaCppModelParams and LiteRtLmModelParams are ModelParams whose
constructors take only the fields their runtime applies, so every API that
takes ModelParams takes them. A load throws LlamaUnsupportedException
when one is used for the other runtime, and their copyWith keeps the type
and throws LlamaArgumentException for the other runtime's fields and
clear* flags.
The liteRtLm* arguments of the ModelParams constructor and copyWith,
and the matching clearLiteRtLm* flags, are deprecated until 1.0. They still
work. The fields keep their names, so reads such as params.liteRtLmCacheDir
are unchanged.
// Before
const params = ModelParams(
liteRtLmCacheDir: '/data/litert-cache',
liteRtLmEmbeddingEngine: true,
device: ComputeDevice.cpu,
);
// After
const params = LiteRtLmModelParams(
cacheDir: '/data/litert-cache',
embeddingEngine: true,
device: ComputeDevice.cpu,
);Deprecated ModelParams argument |
LiteRtLmModelParams argument |
|---|---|
liteRtLmEmbeddingEngine |
embeddingEngine |
liteRtLmActivationDataType |
activationDataType |
liteRtLmPrefillChunkSize |
prefillChunkSize |
liteRtLmParallelFileSectionLoading |
parallelFileSectionLoading |
liteRtLmDispatchLibDir |
dispatchLibDir |
liteRtLmCacheDir |
cacheDir |
liteRtLmMaxProgramCacheBytes |
maxProgramCacheBytes |
copyWith takes the same names and clear* flags (clearCacheDir). As on
ModelParams.copyWith, a clear flag wins over a value for the same field,
whichever spelling either uses. Errors name a LiteRtLmModelParams option
without the prefix. liteRtLmBackend stays deprecated in favor of device.
Native LiteRT-LM now applies GenerationParams.penalty, presencePenalty
and thinkingBudget where engine.capabilities reports them. A value it
cannot apply, such as a penalty below 1.0, a zero ThinkingBudget, a
budget on a bundle without a thinking channel or on a runtime without the
API, throws LlamaUnsupportedException instead of the UnsupportedError
these options threw before. Catch LlamaUnsupportedException, or check
supportsPenalty, supportsPresencePenalty and supportsThinkingBudget
first. A non-finite penalty or presencePenalty (NaN or infinity) throws
ArgumentError instead. Other LiteRT-LM options it does not apply, such as
minP and grammars, still throw UnsupportedError.
LiteRtLmRuntimeClient.generate and generateMessageJson take new optional
named parameters; subclasses that override them must declare them.
The native-assets hook and the LiteRT-LM Apple companion source pin
v0.18.1. A Flutter macOS app loads LiteRT-LM from its own bundle only with
companion 0.16.0, which pins v0.18.1; a companion pinning a v0.18.0
build keeps macOS LiteRT-LM in the hook cache with a build warning. The app
API is unchanged, and native GPU embeddings still throw
LlamaUnsupportedException.
The hook and the stable_diffusion Apple companion source pin v0.3.0, whose
sd_ctx_params_t is larger than in v0.2.0-4. A Flutter iOS or macOS build
needs companion 0.16.0, which pins v0.3.0; upgrade both together. Apps
that depend on a published companion hit pub resolution first, because
companions before 0.16.0 constrain llamadart below 0.16.0. Apps that
depend on llamadart or the companion from git or a path and keep an older
companion fail with Incompatible Apple stable_diffusion companion.
ImageModelParams is unchanged, and flash attention and direct convolution
stay as it selects them.
Upgrade core and every Apple companion used by the app to 0.15.x together.
Companions now constrain core to '>=0.15.0 <0.16.0'. The llama.cpp companion
pins v0.7.0-1; LiteRT-LM and stable_diffusion keep v0.18.0-2 and v0.2.0-3.
cancelGeneration() cancels embedContent / embedContentBatch even
during capability checks on native and Web backends. Handle the existing
LlamaInferenceException cancellation error and await the embedding future
before reuse. Calls started after cancellation remain usable. Native LiteRT-LM
also checks cancellation after worker startup, including its string embedding
methods, which share the content implementation. Native llama.cpp and Web
legacy embed / embedBatch cancellation contracts are unchanged.
Native llama.cpp rejects embeddings (including content and batch embeddings),
next-token scoring, text generation, state save/load and LoRA changes on a
context while text-to-speech is active with LlamaStateException. Await the
synthesis task, including cancellation, before reusing that context. The
reservation starts before speaker-reference loading; a rejected overlapping
request leaves the active request's performance counters intact.
Text-to-speech also rejects a start while generation is active on its context, and a second synthesis on the same native worker, including during setup. This fixes unsafe overlap; method signatures and platform support are unchanged.
The native-assets hook and Apple companion source now pin v0.7.0-1, a
wrapper rebuild of upstream llama.cpp v0.7.0. Existing inference behavior
and the app API are unchanged. Apple apps need companion 0.15.0; the
0.14.0 companion pins v0.7.0 and is rejected by the updated hook.
Keep handling LlamaInferenceException after cancelGeneration(): a runtime
failure racing cancellation now surfaces instead of ending normally. Only the
pinned runtime's exact Task cancelled error after a cancel request ends
normally. The same error without a cancel request remains a failure. No API
migration is needed.
Queued cancellations on native LiteRT-LM now return null diagnostics, matching
llama.cpp, including requests already sent to the worker that never run.
LiteRT-LM validation failures, llama.cpp option-validation failures before
the counter reset, and failed dispatches also return null. Native llama.cpp
resets counters before generation, embedding (the entire batch), next-token scoring
or text-to-speech executes; counters then describe only that request's work.
Request validation failures in the native worker leave diagnostics unavailable;
argument checks that fail before dispatch leave them unchanged. LiteRT-LM still recovers counters
on generation. Requests finishing after reload no longer affect the new context's
counter availability, even if its handle is reused. No API migration is needed;
continue handling unavailable diagnostics. Rejections during active text-to-speech
preserve that synthesis request's counters. After an early cancellation, pending
or failed embedding, scoring and speech calls preserve unavailable diagnostics,
including calls started before cancellation. Generation dispatch restores
availability immediately; embedding, scoring and speech restore it only on
success, using the backend's counter semantics. Web backends still return null
after every request.
An empty tokenizer.chat_template now names the built-in ChatML fallback template in render errors. PEG parse errors retain peg-simple, peg-native
and the historical peg-constructed spelling (still used for Qwen3 Coder XML).
Exception types and parsing behavior are unchanged.
With device: ComputeDevice.auto, splitMode: ModelSplitMode.none and a
negative mainGpu, backend name and capabilities now report CPU, and
getResolvedGpuLayers() returns 0. The model already ran on the CPU;
context and draft-model execution settings are unchanged.
Decision heads now follow this CPU placement too, and
DecisionModelInfo.deviceName reports CPU. Previously, a head could still
select the first matching GPU of the load's backend. Explicit nonnegative
mainGpu selection and other split modes keep their existing head placement.
No parameter migration is needed. Explicit gpu / npu
validation is unchanged.
Native llama.cpp now probes the loaded model/runtime for thinking-budget
support; check supportsThinkingBudget instead of assuming it is available.
Before load or after unload, its generation capability snapshot is empty.
The mtp strategy stays advertised for external draft models; in-file MTP
requires a target model with an MTP head and ModelParams.loadMtp: true.
Missing external draft models now throw LlamaArgumentException at generation
start, preserving the diagnostic instead of wrapping it in an inference error.
Catch LlamaArgumentException for invalid WebGPU request arguments, including
runtime LoRA scales and bridge-reported argument rejections. These previously
surfaced as raw argument errors, context errors or LlamaInferenceException.
scoreNextToken vocabulary and int32 bounds still throw RangeError, matching
native. Runtime inference failures and unsupported capabilities retain their
existing types.
Web llama.cpp content embeddings now accept image/audio bytes and mono 16 kHz
Float32 PCM when bridge assets v0.1.64+ positively probe the modality
(bridge getEmbeddingCapabilities() API version 1; read it with
await engine.embeddingCapabilities). Load a compatible embedding model
and projector first. Paths and image URLs throw LlamaUnsupportedException.
Older or mismatched bridge assets retain text embeddings and report media
unavailable with an upgrade diagnostic. The default pinned assets include the API;
EmbeddingGemma-2 vectors are qualified on Chrome CPU wasm memory64 against
native CPU/Metal; repeated media batches are qualified only on memory64. Hardware WebGPU is
unqualified. Text-only content keeps legacy embed support on pooling NONE.
Image bytes are always encoded data; width/height are ignored for embeddings.
Reserved mixed-input media markers are unsupported, matching native.
The new embedContent / embedContentBatch APIs return one vector per input;
a backend count mismatch throws LlamaInferenceException. Adjacent text parts
form one verbatim-concatenated segment on every backend, including mixed inputs.
The modality enum may grow; include a default case in switches.
Native llama.cpp content embeddings now use the loaded EmbeddingGemma-2
projector for images and audio (v0.7.0-1). Without a projector the probe
reports text only. Oversized expanded media inputs throw
LlamaInferenceException before encoding; M-RoPE projectors, URLs and
ambiguous media sources throw LlamaUnsupportedException. Each media part
needs exactly one encoded bytes/local-file source, or mono 16 kHz audio PCM.
Text-only content follows embed, including causal unpooled models; media
requirements do not restrict text. Older native overrides can retain text
with media disabled; unsupported media errors name the v0.7.0-1 ABI.
No task prefix is inserted. Await a cancelled embedding before reusing the engine.
Windows resolves media functions across the complete native DLL bundle; missing
media symbols disable media without restricting text. EmbeddingGemma-2 BF16
media embeddings are qualified on CPU on macOS arm64, Linux x64, Windows x64, Android and iOS,
and on macOS Metal.
ModelParams.liteRtLmEmbeddingEngine defaults to false for conversation loading.
Set true and device: ComputeDevice.cpu for native LiteRT-LM embeddings. This intentionally
replaces the previous adapter rejection; generation/tokenizer APIs are unavailable
in embedding mode. Native GPU and all Web paths fail with LlamaUnsupportedException.
GGUF loaders still reject true. LiteRT-LM overflow fails the whole call with
LlamaInferenceException and no vectors, possibly after earlier items consumed
compute; it never truncates. llama.cpp retains pre-inference capacity validation.
LiteRT-LM concurrent embeds now queue. Cancellation, unload and timeout discard
the whole batch after the current item settles; timeouts throw
LlamaInferenceException and cancel only that request. Other queued callers retain
their own deadlines; explicit cancellation, unload and dispose cancel all requests.
contextSize selects the encoder signature, without
truncation. Embedding mode rejects conversation tuning (LoRA, chat templates,
activation type, prefill, parallel loading, dispatch paths, program cache limits),
MTP/rollback and Web memory hints rather than silently ignoring them. Native CPU
qualification covers macOS arm64 only.
Upgrade the core and every Apple companion the app uses to 0.14.x together.
If a fresh macOS SwiftPM build reports a generated 12.0 floor, use the
Flutter 3.47.1 bootstrap;
keep Runner and the companion requirements at macOS 14.0 or newer.
The llama.cpp companion now pins native v0.7.0 and the LiteRT-LM companion
pins v0.18.0-2; the image companion keeps its native pin. LiteRT-LM reduces
duplicated words at untimed ASR window boundaries with the default overlap;
other boundary and recognition errors can remain.
Native overrides must match the new v0.7.0 context-parameter and performance-data
layouts as well as its exported symbols. v0.6.0 and older native bundles are
ABI-incompatible with these regenerated bindings. Remove an old native override
or keep the app on the matching core 0.13.x and companions. Apple builds reject
old companion pins before emitting native assets.
Raw responseFormat schemas and LlamaStructuredOutput now share validation.
Unsupported or shadowed assertions that were silently unconstrained now throw a
path-bearing LlamaUnsupportedException before generation. The remaining
unsupported assertions are:
multipleOf,uniqueItems,contains,minContains,maxContains,minProperties,maxProperties,patternProperties,propertyNames,dependentRequired,dependentSchemas,dependencies,not,if,then,else,unevaluatedItems,unevaluatedProperties,additionalItems,$dynamicRefand$recursiveRef, except the trivial assertionsuniqueItems: false,minProperties: 0and integermultipleOf: 1.- Numeric bounds on anything except explicit
type: integer, unsupported formats (anything exceptuuid,uuid1–uuid5,date,time,date-time), patterns outside the compiled subset, and length/item bounds above 2000. - Assertions shadowed by
$ref,anyOf/oneOf, orconst/enum(compatible literaltypeis allowed); pattern or format combined with length bounds; pattern combined with format; andprefixItemscombined withitems. allOfoutside additive object-property merges and nonempty enum intersections: primitive intersections, nested/union components, repeated property names, constrained additional properties (except a single closed object) and shadowed siblings. Components may use local references; an explicit outertypemust beobjectfor property merges orstringfor string enum intersections.- Unresolved/external references, references into annotation/extension payloads,
undeclared
requiredproperties, malformed schemas, conflicting bounds and constraints in the wrong type context. Integer endpoints must fit the exact VM/Web range (absolute value at most 9007199254740991); a weaker inclusive bound cannot shadow a stronger exclusive bound. One-sided positive minima and negative maxima containing a nonfinal zero digit are rejected because upstream loses suffix padding (for example,minimum: 100admits10). At 16 digits, only absolute endpoints matching111111111111111[0-9]are supported; others emit inverted repetition bounds. Two-sided bounds are unaffected.
Integer minimum, maximum, exclusiveMinimum and exclusiveMaximum
(including Pydantic ge= and NegativeInt), the formats above and supported
allOf forms now compile with llama.cpp v0.7.0 semantics. Format validation is
lexical: dates use month/day ranges, and times require a timezone with optional
three-digit fractional seconds. uuid1–uuid5 use the same lexical UUID rule
as uuid, including uppercase hex; they do not constrain the version nibble.
One-sided negative maxima reject leading-zero JSON integers, correcting an
upstream v0.7.0 grammar defect. Arrays with item bounds do not require items;
their unconstrained elements may be any JSON value. Fixed tuples accept both prefixItems and
draft-07 items: [...], with compatible bounds (minItems no greater than the
tuple length, maxItems no smaller). Their grammar still requires every tuple
item. Typed final parsing checks the same tuples, formats and integer bounds.
Object allOf merges make every merged property required in the grammar, as
upstream does; they emit a closed object, matching upstream.
Schemas must contain JSON values, nested at most 128 container levels, including
annotations. Non-JSON values throw LlamaArgumentException. Annotations such as
discriminator, contentMediaType, contentEncoding, example, examples,
$anchor, $id, $schema, $comment, deprecated, readOnly, writeOnly,
title, description, default, and unknown vocabulary (including x-*)
remain accepted and ignored as constraints, just as upstream ignores them.
Only schema-bearing positions are traversed for assertions and references.
Unused $defs/definitions entries are ignored; referenced entries must pass
the same validation as inline schemas.
Nonempty annotation-only untyped schemas accept any JSON value, including in
imported tool properties; {} keeps its existing object grammar. Imported tools
keep their narrower subset and object root (see
tool input schemas).
Decoded tool-call arguments nested beyond 128 containers now fail with
LlamaInferenceException on final and partial parsing instead of overflowing.
Load progress callbacks in LlamaBackend.modelLoadFromUrl, format-routing
loads and the legacy LlamaEngine URL-loading methods now declare
void Function(double progress)? instead of Function(double progress)?.
Existing Function(double progress)? overrides still compile; prefer the
explicit void signature in custom backends and test fakes. Notifications
deliver the same fractions; the return value is ignored and cannot cancel a
load. Native llama.cpp's raw FFI
bool progress callback is unchanged.
Future<int> modelLoadFromUrl(
String url,
ModelParams params, {
void Function(double progress)? onProgress,
}) async {
// Delegate loading to your runtime, forwarding its progress notifications.
return runtime.load(url, onProgress: onProgress);
}For renamed legacy Qwen3 LiteRT-LM text bundles, replace copied Jinja with
ModelParams(chatTemplate: liteRtLmQwen3ChatTemplate) to keep native
enableThinking: false routing stable across upgrades. The accessor always
returns this package version's canonical template. Web
@litert-lm/core@0.18.0 cannot install a conversation template: loading this
canonical override now throws LlamaUnsupportedException instead of appearing
to honor it. Use native LiteRT-LM for this contract. Other Web template overrides
and loads without an override retain their existing behavior.
Cancelling a queued native llama.cpp generation before dispatch now makes
getPerformanceContext() return null until another generation is dispatched.
Handle unavailable diagnostics instead of reusing earlier request counters.
Gemma 2/3/3n and legacy Mistral templates now accept a user message immediately
following tool results. If rendering fails because the template rejects the
tool role, the retry appends that user message to the converted result turn
with a newline. Previously this history threw LlamaInferenceException for
consecutive user turns. Stored history and requests that render successfully
on the first attempt are unchanged. This extends llama.cpp's former tool-result
polyfill; upstream b10549 rejects the original history.
These fixes change previously broken paths; working requests are unchanged.
Apple companions remain at 0.13.0, with the same native and Web runtimes.
- Recursive chat-template failures now throw
LlamaInferenceExceptioninstead ofStackOverflowErroron VM and JavaScript. - Cancelling inside a Hermes tool call keeps only preceding answer text,
discarding unfinished tool markup. With no preceding output,
ChatSession.createreports “Generation was cancelled before any output.” and rolls back the turn. With legacy tools, a completed call before an unfinished second call returnsfinishReason: tool_callswith the completed call, without executing it, instead ofstopwith raw markup. - Unfinished Ministral-format reasoning stays out of reply text when generation stops or is cancelled.
- Renamed native LiteRT-LM Qwen 3 bundles honor
enableThinking: falseonly with a canonical Qwen 3chatTemplateoverride or identifying metadata. A renamed file alone does not identify the template family. - Tool input schemas accept
minLength,maxLength,minItemsandmaxItemsup to 2000, inclusive; 2001 and above still throwLlamaUnsupportedException. Tool input values still obey the declared bounds.
These changes alter what callers see:
responseFormatschemas, raw or typed, that the grammar converter cannot represent throwLlamaUnsupportedException, and cyclic or other non-JSON schemas throwLlamaArgumentException, instead ofStateError,TypeErroror a stack overflow. A combined tool or schema grammar that llama.cpp would refuse fails before generation withLlamaUnsupportedException. Schemas that worked before are unchanged.- LiteRT-LM, native and Web: a generation started right after a cancel waits
for the cancelled run instead of failing. Overlapping a running generation
that was not cancelled, or a second waiting one, throws
LlamaStateExceptioninstead ofLlamaInferenceExceptionwrapping aStateError. - On Web, WebGPU and LiteRT-LM,
cancelGeneration()ends the stream normally instead of surfacingLlamaInferenceExceptionor anAbortError. - A negative
ModelParams.gpuLayersmeans all layers on every runtime, likeModelParams.maxGpuLayers. LiteRT-LM underComputeDevice.autonow selects its GPU backend instead of the CPU; passgpuLayers: 0orComputeDevice.cputo keep the CPU. On native llama.cpp,getResolvedGpuLayers()reports the resolved count instead of the negative value.
Apple companions now share the core MAJOR.MINOR and constrain llamadart
to it, so upgrade every companion the app uses together with the core:
dependencies:
llamadart: ^0.13.0
llamadart_llama_cpp_flutter: ^0.13.0
llamadart_litert_lm_flutter: ^0.13.0
llamadart_stable_diffusion_flutter: ^0.13.0The native pins are unchanged from 0.12.2. An app that takes llamadart
from a path or git checkout repeats that source under dependency_overrides.
LiteRT-LM now caps each cache directory's GPU program cache files at 1 GiB by
default, so the first engine create after upgrading may delete files from an
existing oversized cache, and the runtime recompiles them when needed. Set
ModelParams.liteRtLmMaxProgramCacheBytes to a larger value, or to
ModelParams.unlimitedLiteRtLmProgramCacheBytes (-1) to keep every file.
An explicit cap now bounds the directory total instead of each file.
ToolParam tool grammars are stricter, and PEG parse failures throw
LlamaInferenceException instead of StateError; see the 0.13.0 entry in
CHANGELOG.md.
- On native llama.cpp, a runtime missing an mtmd function, with no complete
fallback mtmd library, makes
supportsVisionand image or audio requests throwLlamaUnsupportedException. Before,supportsVisionreturnedfalseand those requests threwLlamaInferenceException. - On Web,
SpeechToTextEngine.load,TextToSpeechEngine.loadandLlamaEngine.setModelwith a projector now report progress from 0 to 1.
Upgrade the Apple companions the app uses together with the core package:
dependencies:
llamadart: ^0.12.2
llamadart_llama_cpp_flutter: ^0.0.23
llamadart_litert_lm_flutter: ^0.0.16Core 0.12.2 pins llama.cpp v0.6.0-3 and LiteRT-LM v0.18.0-1. llama.cpp
companion 0.0.22 still builds, with a warning: its v0.6.0-2 frameworks lack
the minimum OS version keys that App Store validation checks, so upgrade before
shipping. Any other mismatched llama.cpp companion fails the build with an error naming
0.0.23. With LiteRT-LM companion 0.0.15, the macOS build warns that
LiteRT-LM stays in the hook cache, but the app still loads its arm64 runtime
from the bundle (#1064);
iOS does not check the LiteRT-LM pin. llamadart_stable_diffusion_flutter
0.0.3 is unchanged.
Upgrade the LiteRT-LM companion together with the core package:
dependencies:
llamadart: ^0.12.1
llamadart_litert_lm_flutter: ^0.0.15Core 0.12.1 embeds the macOS LiteRT-LM runtime only through companion
0.0.15; with an older companion the build warns and the runtime stays in the
hook cache. A Flutter macOS app that added the
LiteRT-LM build phase
for 0.12.0 can remove it after upgrading both: the script detects the
embedded runtime and copies nothing, so leaving it in place is harmless.
Other platforms and companions are unchanged.
Upgrade the Apple companions together with the core package:
dependencies:
llamadart: ^0.12.0
llamadart_llama_cpp_flutter: ^0.0.22
llamadart_litert_lm_flutter: ^0.0.14
llamadart_stable_diffusion_flutter: ^0.0.3Add only the companions the app uses. These pairings select llama.cpp
v0.6.0-2, LiteRT-LM v0.18.0 and stable-diffusion.cpp v0.2.0-3.
Keep the previous companions with core 0.11.x; a mismatched llama.cpp or
image companion fails the Apple build's runtime-pin check. Resolve the new
dependencies with flutter pub get.
- Native llama.cpp state files saved by core
0.11.xor earlier no longer load because llama.cppv0.6.0changed their format.stateLoadFilethrowsLlamaStateException; evaluate the prompt again and save a new state file. LlamaEngine.create,ChatSessionandChatTemplateEngine.parseretain trailing reply whitespace in all chat formats. Trim text explicitly when exact comparisons should ignore it.ChatSessionstill removes that whitespace when storing assistant turns, so its next prompt is unchanged.- Multimodal GGUF requests place image and audio parts where their messages put them, and do not duplicate the model's media wrappers. Quoted media placeholder strings stay text; where the runtime would treat one as media, it receives a zero-width space after its first character. That character can appear in the response. Recheck prompts that depended on the old media ordering or wrappers.
- A native projector without the requested image or audio encoder now
throws
LlamaUnsupportedException; update code that caught onlyLlamaInferenceExceptionfor that failure. Invalid or raising chat templates throwLlamaInferenceExceptionwith their template message. - LiteRT-LM engine-creation failures with
ComputeDevice.autoorComputeDevice.cputhrowLlamaModelExceptionon first use. Update code that previously caughtLlamaInferenceExceptionorStateErrorthere. With native LiteRT-LMv0.18.0, custom Qwen 3 templates should accept both string content and typed text-part lists. Native registered string-content bundles receive text again; this repair does not cover Web or the runtime's single-turnis_appending_to_prefillform. See Chat templates. llamadart_native_runtimes: allincludes the image runtime where it is published. Use[llama_cpp, litert_lm]or leave the key unset (without Flutter Edge AI packages, see below) to keep the prior two-runtime selection on other platforms. On Flutter iOS/macOS, addingllamadart_stable_diffusion_flutterselects the image runtime even with that explicit list or an unset key. To omit it on those Apple targets, also omit or remove the image companion dependency. Flutter iOS/macOS need that companion to include the image runtime through SwiftPM when usingall.- Apps whose pubspec, or any member of the same pub workspace, also depends on
flutter_edge_ai_llamaorflutter_edge_ai_litertlmno longer get the LiteRT-LM runtime by default: with the key unset,bothorall, the build hook leaveslitert_lmout and prints awarning: llamadart:line. To keep loading.litertlmmodels through llamadart next toflutter_edge_ai_llama, setllamadart_native_runtimes: [llama_cpp, litert_lm]in the app or workspace-rootpubspec.yaml. Next toflutter_edge_ai_litertlm, naminglitert_lm(or depending onllamadart_litert_lm_flutteron iOS/macOS) fails the build; set[llama_cpp]and remove that companion. See Alongside Flutter Edge AI. - On an iOS simulator
GpuBackend.autonow runs llama.cpp on the CPU (#950), so simulator timings are CPU timings. An explicitGpuBackend.metalstill uses Metal there and logs a warning; iOS devices and macOS are unchanged.
LiteRT-LM automatic tool loops and strict structured output remain unsupported on the pinned runtime. The declared iOS 16.4 deployment floor still needs runtime qualification; these version updates do not establish it.
Image generation is a Preview, and this release changes its API with no
deprecation period, to the pattern every llamadart engine will share: a
model of ModelSource files, params: for runtime settings, download:
for ModelLoadOptions, and generation settings on the request. The library
no longer has model presets or String paths.
-
A model is a main file plus components, each a
ModelSource.ImageGenerationModel.sdxs,sdTurbo,sdxlLightning,flux1Schnell,sd35LargeTurbo,zImageTurboandcustom,ImageGenerationModelFiles,ImageGenerationModelFamilyandImageGenerationModel.familyare gone.loaddownloads each file if needed and assigns its role from its header, so list files in any order:// Before final engine = await ImageGenerationEngine.load( ImageGenerationModel.flux1Schnell( diffusionModelPath: fluxPath, clipLPath: clipLPath, t5xxlPath: t5xxlPath, vaePath: aePath, ), options: const ImageGenerationOptions(device: ImageGenerationDevice.gpu), ); final result = await engine.generateImage( const ImageGenerationRequest(prompt: 'a red fox'), ); // After final engine = await ImageGenerationEngine.load( ImageGenerationModel( ModelSource.path(fluxPath), components: [ for (final path in [aePath, clipLPath, t5xxlPath]) ImageModelComponent.auto(ModelSource.path(path)), ], ), params: const ImageModelParams(device: ComputeDevice.gpu), ); final result = await engine.generateImage( const ImageGenerationRequest( prompt: 'a red fox', width: 1024, height: 1024, steps: 4, guidanceScale: 1, ), );
A file the header check cannot classify, such as a
.ckpt, takes an explicit role:ImageModelComponent(source, role: ImageModelRole.vae), orImageGenerationModel(source, role: ImageModelRole.checkpoint).loadnow refuses LoRA and ControlNet files, two files in one role, and a VAE or TAESD for other latent channels than the diffusion model's. -
Generation settings move to the request.
ImageGenerationDefaultsis gone; an unset request size is 512x512, steps 20 and guidance 7. Set each former preset's values on the request, as in the image generation guide's recipes:0.10.0 preset Files Request settings sdxs(path)the checkpoint steps: 1, guidanceScale: 1sdTurbo(path, taesdPath:)the checkpoint, optional TAESD steps: 1, guidanceScale: 1sdxlLightning(path, vaePath:, taesdPath:)the checkpoint, optional VAE or TAESDXL width: 1024, height: 1024, steps: 4, guidanceScale: 1, sampler: euler, scheduler: sgmUniformflux1Schnell(...)diffusion model, aeor TAEF1, CLIP-L, T5-XXLwidth: 1024, height: 1024, steps: 4, guidanceScale: 1sd35LargeTurbo(...)diffusion model, TAESD3 or VAE, CLIP-L, CLIP-G, T5-XXL width: 1024, height: 1024, steps: 4, guidanceScale: 1zImageTurbo(...)diffusion model, ae, Qwen3llmwidth: 1024, height: 1024, steps: 8, guidanceScale: 1custom(files, defaults:)the same files the former defaultswarmUptakesguidanceScaletoo, so pass the size and guidance you generate with. -
loadtakesparams:,download:,onProgress:andstore:.ImageGenerationOptionsis nowImageModelParams(the engine'soptionsgetter is nowparams), andImageGenerationDevicethe sharedComputeDevice.download:takesModelLoadOptionsfor remote files (cache, auth, retries, cancellation);ModelLoadOptions.sha256verifies a single-file model and throwsLlamaUnsupportedExceptionfor several files, since one checksum cannot cover them.store: ModelFileStore(resolver: ..., downloadManager: ...)replaces the resolver and download manager. -
Errors name files by position, not path. A missing or unusable file is "the main file" or "component N".
LlamaEngine loads like the other engines: LlamaEngine.load creates an
engine and loads a LlamaModel, and setModel loads or replaces the model
of an engine you already have. loadModel, loadModelSource,
loadModelFromUrl and loadMultimodalProjector are deprecated and keep
working until 1.0.
-
Load a model and its projector in one call. Runtime settings are
params:and download settings aredownload:. The load is atomic: when it throws, the engine and its backend are disposed and nothing stays loaded.// Before final engine = LlamaEngine(LlamaBackend()); await engine.loadModelSource( ModelSource.parse('hf://owner/repo/model.gguf'), modelParams: const ModelParams(contextSize: 4096), options: ModelLoadOptions(bearerToken: token), onProgress: (progress) => print(progress.fraction), ); try { await engine.loadMultimodalProjectorSource( ModelSource.parse('hf://owner/repo/mmproj.gguf'), ); } catch (_) { await engine.unloadModel(); rethrow; } // After final engine = await LlamaEngine.load( LlamaModel( ModelSource.parse('hf://owner/repo/model.gguf'), projector: ModelSource.parse('hf://owner/repo/mmproj.gguf'), ), params: const ModelParams(contextSize: 4096), download: ModelLoadOptions(bearerToken: token), onProgress: (progress) => print(progress.fraction), );
A local file is
ModelSource.path(path), andModelSource.parsetakes a path, anhttp(s)URL or anhf://reference. A custom resolver or download manager goes instore: ModelFileStore(...).onProgressreports the model and its projector together: with a projector,totalBytesandfractionare null until the model has downloaded and the projector reports its size, so showreceivedBytesuntil then. -
Switch models with
setModel. It replaces the loaded model, so theunloadModel()before a second load is no longer needed. The loaded model keeps serving until every file of the new one has downloaded; a download that fails or is cancelled leaves it loaded. A load that fails after that leaves nothing loaded. On the Web the runtime fetches the files itself, sosetModelunloads the loaded model before the fetch, and a fetch that fails leaves nothing loaded.// Before await engine.unloadModel(); await engine.loadModel('/models/other.gguf', modelParams: params); // After await engine.setModel( LlamaModel(ModelSource.path('/models/other.gguf')), params: params, );
While
setModelruns, anothersetModelandunloadModel()throwLlamaStateException; stop it withdownload's cancel token.dispose()stops its downloads at once, and the call throwsLlamaStateException. -
loadMultimodalProjectorSourcetakesdownload:.options:is the deprecated name; passing both throwsLlamaArgumentException. Use the method to change the projector of a loaded model; load a model with its projector as in step 1.unloadModel()anddispose()now stop its download, and the load throwsLlamaStateExceptioninstead ofLlamaContextException. -
What differs from the deprecated loaders.
- A local file takes only
download's cancel token and, for a model without a projector, itssha256.loadModelSourcethrewLlamaUnsupportedExceptionfor a local path with a bearer token, headers, cache directory, cache policy, resume or retry setting;loadandsetModeldo not apply them to a local file. download's bearer token and headers go to one origin only: a model and projector on different hosts throwLlamaArgumentExceptionwhen they are set.ModelLoadOptions.sha256with a projector throwsLlamaUnsupportedException, since it cannot name two files.- A projector for a LiteRT-LM model and
ComputeDevice.npufor a GGUF throwLlamaUnsupportedExceptionbefore anything downloads, when theModelSource.formator file name gives the format. - On the Web, a
ModelSource.pathis a URL relative to the document, or ablob:URL. It used to throwLlamaUnsupportedException, and now loads in the deprecatedloadModelSource,setLoraSourceand draft models too. - A subclass that overrides
loadModelno longer sees loads made throughloadandsetModel. Fake aLlamaBackendin tests, or overridesetModel. loadandsetModelcheck a local file through the download manager before the backend sees it, so a test that loads a made-up path such asmodel.ggufinto a fake backend now throwsLlamaModelException(Local model file does not exist). Give the test a real temporary file, or a fakeModelDownloadManagerinstore:orLlamaEngine(backend, modelDownloadManager: ...).- A
blob:URL goes inModelSource.path;ModelSource.parseaccepts only paths,http(s)URLs andhf://references. - A
LlamaEngineObserversees a wholeloadorsetModelcall as one model load, with its downloads, so a file that is missing or fails to download ends that operation with an error.loadModelSourcereports only the load that follows its download.
- A local file takes only
-
New members on
LlamaEngine. A class thatimplements LlamaEnginemust addsetModel, and an override ofloadMultimodalProjectorSourcemust add thedownloadparameter and makeoptionsnullable. -
Messages. A request before a load throws
LlamaContextExceptionwithEngine not ready: no model is loaded. Call LlamaEngine.load or setModel first.Code that matched the earlier text should catch the exception type instead.
ImageGenerationTask, SpeechToTextTask and TextToSpeechTask share one
shape: events carries progress only, done reports how the task ended and
never throws, the new result returns the result or throws, and cancel()
stops only that task.
-
ImageGenerationEngine.generatereturns aFuture(Preview). Await it for the task; invalid requests, a disposed engine and a running generation now fail that future instead of throwing synchronously. The one-generation slot is still taken whengenerateis called.// Before final task = engine.generate(request); // After final task = await engine.generate(request);
-
Task
eventsno longer carry errors. A failed task closeseventswithout a final event. Code that caught the failure from the stream (anonErrorhandler, ortryaroundawait for) reads it fromresultordoneinstead:// Before try { await for (final event in task.events) { if (event is TextToSpeechFinalEvent) save(event.result); } } on LlamaException catch (error) { report(error); } // After task.events.listen(showProgress); try { save(await task.result); } on LlamaException catch (error) { report(error); // LlamaStateException when the task was cancelled }
SpeechToTextStreamingSessionis unchanged: itseventsstill report a failure as a stream error as well as throughdone, and itscancel()returns aFuture, because it ends a live input stream rather than one result. -
SpeechToTextTask.cancel()stops only its own task. A prompt-adapter task used to callLlamaEngine.cancelGeneration, which also ended chat, tool-loop and other requests on the same engine. It now cancels only the task's own generation. CallLlamaEngine.cancelGenerationyourself if you relied on that.
DecisionEngine follows the shared engine pattern, and the String-path
DecisionEngine.load(engine, headPath:, configPath:) is removed.
-
Keep your engine: switch to
attach. The one-line change:// Before final decisions = await DecisionEngine.load( engine, headPath: headPath, configPath: configPath, ); // After final decisions = await DecisionEngine.attach( engine, head: ModelSource.path(headPath), config: configPath == null ? null : ModelSource.path(configPath), );
The head and config now resolve through the engine's download manager, so
hf://and URL sources work too, and a missing local file throwsLlamaModelExceptionbefore the head loads.dispose()still frees only the head. On Web a path is still a URL resolved against the document base URL; ablob:URL goes inModelSource.pathtoo. -
Or let
loadown the engine. Describe the files asModelSources;loaddownloads them, loads the encoder with the 512-token context decisions need, and loads the head.dispose()frees everything:// Before final engine = LlamaEngine(LlamaBackend()); await engine.loadModelSource( encoder, modelParams: const ModelParams(contextSize: 512, gpuLayers: 0), ); final headFile = await engine.modelDownloadManager.ensureModel(head); final decisions = await DecisionEngine.load( engine, headPath: headFile.filePath, ); // ... await decisions.dispose(); await engine.dispose(); // After final decisions = await DecisionEngine.load( DecisionModel(encoder: encoder, head: head), params: const DecisionModelParams(device: ComputeDevice.cpu), ); // ... await decisions.dispose();
configPath:becomesDecisionModel.config.download:takesModelLoadOptions,store:aModelFileStore(for exampleModelFileStore(downloadManager: myManager)), andonProgressreports all files together. Read the backend name fromawait decisions.capabilities. To load an encoder yourself forattach, useconst DecisionModelParams().encoderModelParams. -
Missing-config hint. The error for a head without
laya.configmetadata now asks for "the head's rl_agent_config.json as its config" instead of namingconfigPath; match onlaya.configif you parse it.
LlamaEngine.create, createStructuredJson and chatTemplate deprecate
sourceLangCode and targetLangCode. Pass the codes in
chatTemplateKwargs, as llama.cpp's chat_template_kwargs does; the
parameters keep working, with deprecation warnings, until 1.0:
// Before
engine.create(messages, sourceLangCode: 'en', targetLangCode: 'ko');
// After
engine.create(
messages,
chatTemplateKwargs: const {'source_lang_code': 'en', 'target_lang_code': 'ko'},
);A code passed as a parameter replaces the same key in chatTemplateKwargs.
A custom BackendNativeChatGeneration that read sourceLangCode or
targetLangCode in generateChat gets them from LlamaEngine only in
chatTemplateKwargs now.
No source change is required. On Android and iOS, LlamaEngine,
ModelDownloadController and DefaultModelDownloadManager() (and auto()
without a mobile directory) now cache models in llamadart/models under the
app's cache directory, the one Flutter's getApplicationCacheDirectory()
returns, instead of Directory.systemTemp/llamadart/models. Models cached
under the old path download once more; the old copies are left for the OS to
clear. Apps that already pass a directory are unaffected. To pick another
directory for every default download, set
DefaultModelDownloadManager.globalCacheDirectory before the first load.
-
Argument and state errors join the
LlamaExceptionhierarchy. Catch the new types, orLlamaExceptionfor all of them:Call Before After ModelParams.validate()ArgumentErrorLlamaArgumentExceptionA backend's own model load or context create with invalid ModelParamsArgumentErrororExceptionLlamaArgumentExceptionModelDownloadController.startwithoptions.cancelTokenArgumentErrorLlamaArgumentExceptionModelDownloadController.startwhile a task runs,retrybeforestart, either afterdisposeStateErrorLlamaStateExceptionWeb LlamaBackend()orWebGpuLlamaBackendcalls before a model loadStateErrorLlamaStateException// Before try { params.validate(); } on ArgumentError catch (e) { print(e.name); } // After try { params.validate(); } on LlamaArgumentException catch (e) { print(e.name); // also e.invalidValue and e.message }
Model loads already reported invalid
ModelParamsasLlamaModelException, and still do. -
A failed load's
detailsis aString.LlamaModelException.detailsfromloadModel,loadModelFromUrl,loadModelSourceandloadMultimodalProjectorwas a{type, message}map; it is now the cause's message, such asModel file not found: /models/m.gguf, with URL secrets still redacted. -
One logging API.
LlamaLogging.configuresets the Dart-side level, the native level (defaulting to the Dart-side level) and the handler for the whole library. The old calls keep working, with deprecation warnings, until 1.0:Before After LlamaEngine.configureLogging(level: l, handler: h)LlamaLogging.configure(level: l, nativeLevel: n, handler: h)engine.setLogLevel(l)LlamaLogging.configure(level: l)engine.setDartLogLevel(d)+engine.setNativeLogLevel(n)LlamaLogging.configure(level: d, nativeLevel: n)engine.dartLogLevel,engine.nativeLogLevelLlamaLogging.level,LlamaLogging.nativeLevelconfigurereplaces the handler too, so pass it on every call that should keep it. Levels are now library-wide:engine.setNativeLogLevelon one engine changes every engine, andLlamaEngine.configureLoggingalso updates running worker isolates.
SpeechToTextEngine and TextToSpeechEngine follow the shared engine
pattern: load takes a model of ModelSource files and owns what it loads,
and an adapter, not a profile enum, says how to run the model. The old
constructors, SpeechToTextModelProfile, TextToSpeechModelProfile and the
modelProfile getters keep working, with deprecation warnings, until 1.0.
-
Load the model, or attach to an engine you keep.
loadcreates aLlamaEngine, loads the model and projector, checkscapabilities, and throwsLlamaUnsupportedExceptionwhen the model cannot recognize speech.dispose()then disposes that engine:// Before final engine = LlamaEngine(LlamaBackend()); await engine.loadModel('/models/Qwen3-ASR-0.6B-Q8_0.gguf'); await engine.loadMultimodalProjector('/models/mmproj-Qwen3-ASR-0.6B-Q8_0.gguf'); final recognizer = SpeechToTextEngine( engine, modelProfile: SpeechToTextModelProfile.qwen3Asr, ); // After final recognizer = await SpeechToTextEngine.load( SpeechToTextModel( ModelSource.path('/models/Qwen3-ASR-0.6B-Q8_0.gguf'), projector: ModelSource.path('/models/mmproj-Qwen3-ASR-0.6B-Q8_0.gguf'), adapter: const Qwen3AsrAdapter(), ), ); try { final result = await recognizer.transcribeOnce(request); } finally { await recognizer.dispose(); }
Remote sources download into the model cache.
download:takesModelLoadOptionsfor every remote file andonProgress:reports the files together (on Web, where the browser fetches each file, only the model's fetch reports progress, up to 0.5 with a projector); a local file takes only the cancel token, andbearerTokenandheadersgo to one host only, so remote files on two hosts with them set throwLlamaArgumentException.ModelLoadOptions.sha256throwsLlamaUnsupportedException, since one checksum cannot cover two files.params:takesModelParams,store:aModelFileStorewith your own resolver or download manager, andbackend:theLlamaBackend, which the speech engine then owns and disposes. Whenloadthrows, nothing stays loaded.To share a
LlamaEngineyou load yourself, for example with chat, useSpeechToTextEngine.attach(engine, adapter: const Qwen3AsrAdapter()). Itsdispose()cancels its task and leaves the engine loaded. -
Dedicated LiteRT-LM ASR is a model with a
LiteRtLmAsrAdapter. The runtime settings ofLiteRtLmAsrRuntimeConfigandlibraryPathmove to the adapter, and the files becomeModelSources:// Before final recognizer = SpeechToTextEngine.liteRtLm( const LiteRtLmAsrRuntimeConfig( modelPath: '/models/moonshine_tiny.tflite', tokenizerPath: '/models/tokenizer.json', modelPreset: LiteRtLmAsrModelPreset.moonshineTiny, ), ); // After final recognizer = await SpeechToTextEngine.load( SpeechToTextModel( ModelSource.path('/models/moonshine_tiny.tflite'), tokenizer: ModelSource.path('/models/tokenizer.json'), adapter: const LiteRtLmAsrAdapter(LiteRtLmAsrModelPreset.moonshineTiny), ), );
loadprobes the runtime before it downloads anything and throwsLlamaUnsupportedExceptionwhere it is unavailable, including on the web, whereSpeechToTextEngine.liteRtLmreturned a recognizer whosecapabilitiesreported unsupported.params:andbackend:must be null. The model and tokenizer can be URLs or Hugging Face files:loaddownloads them withdownload:andonProgress:as in step 1.SpeechToTextEngine.liteRtLmopens local files only and throwsLlamaUnsupportedExceptionfor a remote source. -
Text to speech takes a
TextToSpeechModel.// Before final synthesizer = TextToSpeechEngine( engine, modelProfile: TextToSpeechModelProfile.qwen3Tts, ); // After, loading the files final synthesizer = await TextToSpeechEngine.load( TextToSpeechModel( ModelSource.parse('hf://owner/repo/tts-model.gguf'), projector: ModelSource.parse('hf://owner/repo/mmproj-tts-model.gguf'), adapter: const Qwen3TtsAdapter(), ), ); // After, keeping your engine final synthesizer = TextToSpeechEngine.attach( engine, adapter: const Qwen3TtsAdapter(), );
-
Profiles become adapters.
Before After SpeechToTextEngine(engine, modelProfile: SpeechToTextModelProfile.qwen3Asr)SpeechToTextEngine.attach(engine, adapter: const Qwen3AsrAdapter()), orloadSpeechToTextEngine.liteRtLm(config, libraryPath: path)SpeechToTextEngine.load(SpeechToTextModel(model, tokenizer: tokenizer, adapter: LiteRtLmAsrAdapter(preset, libraryPath: path)))TextToSpeechEngine(engine, modelProfile: TextToSpeechModelProfile.qwen3Tts)TextToSpeechEngine.attach(engine, adapter: const Qwen3TtsAdapter()), orloadrecognizer.modelProfile,synthesizer.modelProfilerecognizer.adapter,synthesizer.adapterThe deprecated
modelProfilegetters throwLlamaStateExceptionfor an adapter with no profile, such as your ownSpeechToTextPromptAdapter. -
Dispose the speech engine.
dispose()is new: it cancels a running task or stream, waits for it to stop, and disposes the engineloadcreated. It is safe to call twice. Afterwardstranscribe,startStreamandsynthesizethrowLlamaStateException, andcapabilitiesreports unsupported. Code that used the deprecated constructors and disposed theLlamaEngineitself keeps working. A class thatimplementsSpeechToTextEngineorTextToSpeechEngine, such as a test fake, must adddispose(),isDisposed,adapterandtranscribeOnceorsynthesizeOnce.
LoRA adapters, speculative draft models and LiteRT-LM ASR files take a
ModelSource, so a remote file downloads into the model cache like a model.
The String path forms keep working, with deprecation warnings, until 1.0:
| Before | After |
|---|---|
engine.setLora(path, scale: s) |
engine.setLoraSource(ModelSource.path(path), scale: s) |
engine.removeLora(path) |
engine.removeLoraSource(ModelSource.path(path)) |
LoraAdapterConfig(path: path, scale: s) |
LoraAdapterConfig.source(ModelSource.path(path), scale: s) |
SpeculativeDecodingConfig.draftSimple(draftModelPath: path) (and the other constructors) |
SpeculativeDecodingConfig.draftSimple(draftModel: ModelSource.path(path)) |
LiteRtLmAsrRuntimeConfig(modelPath: m, tokenizerPath: t, ...) |
LiteRtLmAsrRuntimeConfig.source(model: ModelSource.path(m), tokenizer: ModelSource.path(t), ...); to recognize speech, SpeechToTextEngine.load as in the speech migration |
// Before
await engine.setLora('/models/lora/domain.gguf', scale: 0.7);
final config = LiteRtLmAsrRuntimeConfig(
modelPath: '/models/moonshine_tiny.tflite',
tokenizerPath: '/models/tokenizer.json',
modelPreset: LiteRtLmAsrModelPreset.moonshineTiny,
);
// After
await engine.setLoraSource(
ModelSource.path('/models/lora/domain.gguf'),
scale: 0.7,
);
final config = LiteRtLmAsrRuntimeConfig.source(
model: ModelSource.path('/models/moonshine_tiny.tflite'),
tokenizer: ModelSource.path('/models/tokenizer.json'),
modelPreset: LiteRtLmAsrModelPreset.moonshineTiny,
);- A local path, relative ones included, stays
ModelSource.path(path). On WebGPU, where these paths were URLs, useModelSource.parse(url); a local path there throwsLlamaUnsupportedException. - For a page-relative URL on the web (such as
models/adapter.gguf), resolve it against the page:ModelSource.url(Uri.base.resolve(path)). ModelSourceandconst:ModelSource.pathis not aconstconstructor, so dropconstfrom aModelParams,GenerationParamsorLiteRtLmAsrRuntimeConfigthat now holds one.- Remove an adapter with the same source it was set from:
removeLoraSourcematches sources, not paths. draftModelDownload,setLoraSource(download:, onProgress:)andLoraAdapterConfig.source(source, download:)set the download options for remote files. Remote LiteRT-LM ASR files download throughSpeechToTextEngine.load;LiteRtLmAsrRuntimeConfigholds local files. AModelParams.lorasadapter never takes the model load's bearer token, headers orsha256; give it its owndownload:when its host needs credentials.- A draft model downloads once per loaded model;
draftModelDownloadrejectsModelCachePolicy.noCacheandrefresh.
ToolDefinition.handler is a ToolHandler?, so a tool the app runs itself
can leave it out. A direct call to the handler no longer compiles: call
invoke, which throws LlamaStateException for a tool without a handler, or
null-check handler:
// Before
final result = await tool.handler(ToolParams(args));
// After
final result = await tool.invoke(args);package:llamadart/llamadart.dart is now the app API only. Code that loads
models, generates and uses the speech, image and decision engines needs no
change. Custom backends, backend test fakes and raw runtime access import one
of two new libraries.
-
Raw llama.cpp FFI:
package:llamadart/llama_cpp_bindings.dart. The ffigen bindings (llama_backend_init,llama_decode,ggml_*,mtmd_*and their structs) are no longer in the app API. They are native only, and any release that updates llama.cpp can change them:import 'package:llamadart/llama_cpp_bindings.dart'; import 'package:llamadart/llamadart.dart';
-
Backend SPI:
package:llamadart/backend.dart. These names move there:BackendAvailability,BackendBatchEmbeddings,BackendDartLogLevel,BackendDecision,BackendDecisionCapabilities,BackendDecisionHeadInfo,BackendDecisionOutput,BackendDecisionSequence,BackendEmbeddings,BackendEmbeddingsSupport,BackendGenerationCapabilities,BackendGenerationCapabilitiesSupport,BackendGpuEnumeration,BackendGrammarConstraintsSupport,BackendLazyGrammarSupport,BackendModelFileTypeDiagnostics,BackendNativeChatGeneration,BackendNextTokenScoring,BackendNextTokenScoringSupport,BackendPerformanceDiagnostics,BackendPromptSpeechToTextSupport,BackendRuntimeDiagnostics,BackendStatePersistence,BackendStatePersistenceSupport,BackendTextToSpeech,BackendTextToSpeechCapabilities,BackendTextToSpeechPhase,BackendTextToSpeechProgress,BackendTextToSpeechRequestandBackendTextToSpeechResult.LiteRtLmBackend,LiteRtLmRuntimeClient,LiteRtLmRuntimeMetricsandLiteRtLmRuntimeResult.LiteRtLmAsrRuntimeSession,LiteRtLmAsrPushResult,LiteRtLmAsrProcessResultandLiteRtLmAsrProcessState.
LlamaBackend,BackendPerfContextData,BackendTextToSpeechModel,StateLoadResult,LiteRtLmAsrBackend,LiteRtLmAsrModelPresetandLiteRtLmAsrRuntimeConfigstay in the app API;backend.dartexports the first four too. Add the import next to the app one:import 'package:llamadart/backend.dart'; import 'package:llamadart/llamadart.dart';
-
Engine hooks are extension members.
modelHandle,contextHandle,backendTextToSpeechCapabilities,synthesizeTextToSpeechBackend,cancelTextToSpeechBackend,backendDecisionCapabilities,loadDecisionHeadBackend,runDecisionBackendandfreeDecisionHeadBackendmove fromLlamaEngineto theLlamaEngineBackendHooksextension inbackend.dart. Statically typed calls keep working oncebackend.dartis imported. Extension members resolve at compile time, so a call throughdynamic, such as(engine as dynamic).modelHandle, throwsNoSuchMethodError; type the receiver asLlamaEngine. ALlamaEnginesubclass that overrode one no longer intercepts it, and the analyzer reports only anoverride_on_non_overriding_memberwarning. A fake thatimplements LlamaEngineis bypassed the same way wherever it is typed asLlamaEngine, as insideTextToSpeechEngineandDecisionEngine: the extension runs instead of the fake's members and reads engine state the fake lacks, so the call fails withNoSuchMethodError. In both cases, fake at the backend instead:// Before class FakeEngine extends LlamaEngine { FakeEngine() : super(LlamaBackend()); @override Future<BackendTextToSpeechResult> synthesizeTextToSpeechBackend( BackendTextToSpeechRequest request, { void Function(BackendTextToSpeechProgress progress)? onProgress, }) async => fakeResult; } // After class FakeBackend implements LlamaBackend, BackendTextToSpeech { @override Future<BackendTextToSpeechResult> synthesizeTextToSpeech( int contextHandle, int mmContextHandle, BackendTextToSpeechRequest request, { void Function(BackendTextToSpeechProgress progress)? onProgress, }) async => fakeResult; // ... the rest of LlamaBackend and BackendTextToSpeech }
-
Removed deprecated APIs.
LiteRtLmBenchmarkClient,LiteRtLmBenchmarkMetricsandLiteRtLmBenchmarkResultare gone; useLiteRtLmRuntimeClient,LiteRtLmRuntimeMetricsandLiteRtLmRuntimeResult.LiteRtLmRuntimeClient.conversationTokenCountandreplaceConversationWithCloneare gone with no replacement.
ModelParams.device takes a ComputeDevice and applies to llama.cpp and
LiteRT-LM, as ImageModelParams.device and DecisionModelParams.device
already do. ModelParams.liteRtLmBackend, LiteRtLmBackendPreference and
LiteRtLmBackend(preferredBackend:) are deprecated and keep working until
1.0.
-
Replace the LiteRT-LM selector with
device.// Before final engine = LlamaEngine(LiteRtLmBackend(preferredBackend: 'gpu')); await engine.loadModel( path, modelParams: const ModelParams( liteRtLmBackend: LiteRtLmBackendPreference.gpu, ), ); // After final engine = await LlamaEngine.load( LlamaModel(ModelSource.path(path)), params: const ModelParams(device: ComputeDevice.gpu), );
ComputeDevice.auto, the default, keeps each runtime's default device, so code that sets neither field is unchanged. Settingdevicetogether withliteRtLmBackendthrowsLlamaArgumentException; setting it withLiteRtLmBackend(preferredBackend:)throwsLlamaUnsupportedException. Code that still constructsLiteRtLmBackendimports it frompackage:llamadart/backend.dart, as the section above describes. -
An explicit device is a requirement. With
device: cpu,gpuornpu, a device the runtime and platform cannot provide throwsLlamaUnsupportedException, not theLlamaModelExceptionthe deprecated selector throws, and nothing falls back to another device:- llama.cpp
gpuwithout a GPU module or device, whichpreferredBackendused to load on the CPU with a warning, now throws; on Androidgpuuses Vulkan, whichautodoes not. - On the Web,
gpuneeds a WebGPU adapter, and the llama.cpp bridge no longer retries a failed GPU load on the CPU. - Native LiteRT-LM creates its engine on first use, so a GPU or NPU delegate that fails to start throws from the first generation or tokenizer call.
Use
ComputeDevice.autoto accept the runtime's default, or catchLlamaExceptionto cover the old and new types. - llama.cpp
-
One
devicecovers both runtimes. Underauto,gpuLayersandpreferredBackendstill choose the LiteRT-LM backend as before. To run llama.cpp on the CPU and LiteRT-LM on the GPU from oneModelParams, keep the deprecatedliteRtLmBackendwithdevice: auto, or pick the params fromModelSource.format. -
ModelParams.validate()runs before the download.loadModel,loadModelSourceandloadModelFromUrlcall it first, so an invalid combination throwsLlamaArgumentExceptionbefore anything downloads, where it used to fail the backend load asLlamaModelException. New rules rejectdevice: cpuwith a GPUpreferredBackend, andgpuornpuwith a CPU or BLASpreferredBackend,gpuLayers: 0, orsplitMode: ModelSplitMode.nonewith a negativemainGpu. -
Decision models.
DecisionModelParams.encoderModelParamsnow carriesdeviceinstead ofpreferredBackend: GpuBackend.cpuandgpuLayers: 0forcpu.DecisionModelParams(device: ComputeDevice.gpu)no longer asks the backend for GPU support before loading: the encoder loads on a GPU or the load throwsLlamaUnsupportedException.
-
Image capabilities are async;
runtimeCapabilities()is removed.ImageGenerationEngine.capabilitiesreturns aFuture, like every other engine's. It changes only when the engine is disposed, so read it once afterloadand keep it, for example in a FlutterState, instead of reading it inbuild. Probe the runtime before loading withcheckRuntime(), which runs off the calling isolate:// Before final runtime = ImageGenerationEngine.runtimeCapabilities(); print(engine.capabilities.backendName); // After final runtime = await ImageGenerationEngine.checkRuntime(); final capabilities = await engine.capabilities; print(capabilities.backendName);
ImageGenerationCapabilitiesandDecisionCapabilitiesnow implementEngineCapabilities, so code that readsisSupported,unsupportedReasonandbackendNamecan take any engine's capabilities. -
LlamaEngine.dispose()is terminal. A second call returns the same future instead of disposing the backend again. After it, a load or request throwsLlamaStateException(requests used to throwLlamaContextException),DecisionEngine.attachthrowsLlamaStateException, andcapabilitiesreports the engine as disposed. The backend queriesgetBackendName,getAvailableBackends,isGpuSupported,getVramInfo,listGpuDevicesandgetResolvedGpuLayers, which used to answer afterdispose, now throwLlamaStateException; read them before disposing. Model queries such asgetMetadataandgetContextSizereturn their no-model values, as before.unloadModelandcancelGenerationdo nothing. To switch models, callsetModel; to start over afterdispose, create a newLlamaEngine. A load running whendisposeis called now throwsLlamaStateExceptioninstead of completing, and its model is unloaded. A class thatimplements LlamaEnginemust addbool get isDisposed. -
supportsVisionandsupportsAudioagree withcapabilities. On a LiteRT-LM bundle that takes media directly, they are now true without a multimodal projector. When the runtime cannot probe a projector, they are false instead of throwingLlamaUnsupportedException.
-
Flutter iOS/macOS apps update the companion packages with the core. Core
0.11.0pairs withllamadart_llama_cpp_flutter0.0.21,llamadart_litert_lm_flutter0.0.13andllamadart_stable_diffusion_flutter0.0.2. A^0.0.xconstraint admits only that version, so edit each one the app uses:dependencies: llamadart: ^0.11.0 llamadart_llama_cpp_flutter: ^0.0.21 llamadart_litert_lm_flutter: ^0.0.13 llamadart_stable_diffusion_flutter: ^0.0.2
An Apple build that resolves an older llama.cpp or stable_diffusion companion fails with
Incompatible Apple ... companion. -
More members for a class that
implementsan app type.ChatSessiongainscreateStructuredJson, andLlamaEnginegainsruntime,capabilities,setLoraSourceandremoveLoraSource, besidessetModelandisDisposedabove. -
An unknown
responseFormattype or key throws. A map with a misspelledtypeor key, such asjson_shemaorschma, used to generate unconstrained output. It now throwsLlamaUnsupportedExceptionbefore generation; anull-valued key counts as absent. -
Native
LlamaBackend()picks the runtime from the file header. An extensionless download now loads in the runtime its header names. A recognized header that contradicts the file's model extension or an explicitformat:throwsLlamaModelFormatException; rename the file or pass the matchingformat:.
No public signature changes, but several calls now return or throw something different. Check each item that your app uses.
-
ModelParams.chatTemplatenow drives llama.cpp chat. Before, native llama.cpp and WebGPU ignored it inLlamaEngine.create,LlamaEngine.chatTemplateandChatSession, and used the GGUFtokenizer.chat_template. Now a non-empty value renders the prompt and selects the tool-call and reasoning parser. If you set it only as a fallback, passnullto keep the GGUF template. The value is Jinja source, so a name such aschatmlis not mapped to a built-in template. -
WebGPU
LlamaBackend.applyChatTemplatethrows for a template override. Before, it ignoredcustomTemplateandModelParams.chatTemplateand returnedrole: contentlines. Now it throwsLlamaUnsupportedException. Render throughLlamaEngine.chatTemplateorLlamaEngine.createinstead, which apply the override in Dart on every backend. -
LlamaCompletionChunk.modelis the file name. Before, it held the load source: the full local path, the cache path, or the redacted URL. Now it is the last path segment, such asqwen.gguf, orllama_modelwhen there is none or it contains URL syntax such as?,#or@(data:andblob:URLs reportllama_modeltoo). Compare it with the file name instead of the path:// Before if (chunk.model == modelPath) { ... } // After if (chunk.model == path.basename(modelPath)) { ... }
-
%in local and cache paths stays literal. Before,loadModelSourceandModelCacheEntrypercent-decoded paths, soqwen%2541.ggufloaded a different file and a lone%threwArgumentError. Now the path is used as written: pass the path as it is on disk, not percent-encoded. -
ModelParams.lorasis applied at load on native llama.cpp and WebGPU. Before, those backends ignored it and loaded the base model. Now each adapter is applied in order with its scale, and a load whose adapter cannot be applied fails:LlamaUnsupportedExceptionfor an unsupported adapter or WebGPU bridge assets without LoRA support (the minimum version is in the LoRA adapters guide), otherwiseLlamaModelExceptionwith the adapter indetails. Remove adapters you did not mean to apply, and catch both exceptions aroundloadModel. -
LiteRT-LM
ModelParamsrejections throwLlamaUnsupportedException. Before, native and web LiteRT-LM wrapped them inLlamaModelException. This covers more than one LoRA adapter and a non-default adapter scale.// Before } on LlamaModelException catch (e) { ... } // After } on LlamaUnsupportedException catch (e) { // ModelParams option this runtime does not support. } on LlamaModelException catch (e) { ... }
-
Media without a projector throws. Before, image or audio parts sent to a GGUF model with no projector loaded were answered from the text alone on native llama.cpp and failed as
LlamaInferenceExceptionon WebGPU. Now both throwLlamaUnsupportedException, and native llama.cpp and LiteRT-LM throw it forLlamaImageContent.url. Load a projector withloadMultimodalProjectorfirst, or drop media parts from requests and history when none is loaded; pass image bytes or a file instead of a URL.
No source migration is required for existing calls: DefaultModelDownloadManager
constructors remain source-compatible, and the mobile-specific directory
arguments added to DefaultModelDownloadManager.auto(...) in 0.8.10 are
optional.
There are two intentional runtime default changes to be aware of:
-
DefaultModelDownloadManager()no longer defaults to the process temporary directory on desktop/server platforms. It now uses the same platform cache root asDefaultModelDownloadManager.auto():Platform New default root Linux $XDG_CACHE_HOME/llamadart/models, or$HOME/.cache/llamadart/modelswhenXDG_CACHE_HOMEis unsetmacOS $HOME/Library/Caches/llamadart/modelsWindows %LOCALAPPDATA%\llamadart\models, then%APPDATA%\llamadart\models, then%USERPROFILE%\AppData\Local\llamadart\modelsIf a desktop/server embedder cannot expose a home/cache environment, the default constructor preserves compatibility by falling back to
Directory.systemTemp/llamadart/models. Explicitauto(...)andsharedCache(...)calls still report cache-resolution errors so apps can choose a durable directory. -
DefaultModelDownloadManager.auto(platform: android/ios)without an explicit mobile directory no longer throws. It now uses an app-private temporary/cache fallback atDirectory.systemTemp/llamadart/models. This is convenient for examples and rebuildable downloads, but large durable mobile model files should still use an app-private cache/support directory resolved by the app. For Flutter apps, preferpath_provider.getApplicationCacheDirectory()for re-downloadable model caches; usegetApplicationSupportDirectory()only for app-owned durable support files when the app also accounts for platform backup/no-backup policy.
Recommended cross-platform setup:
final engine = LlamaEngine(
LlamaBackend(),
modelDownloadManager: DefaultModelDownloadManager.auto(
// On Flutter, pass a path resolved by path_provider for the current app.
// For re-downloadable model caches, prefer getApplicationCacheDirectory().
// Desktop/server ignores this and uses the per-user shared cache.
appPrivateCacheDirectory: appCacheModelsDirectory,
),
);If your app resolves platform-specific mobile directories ahead of time, pass
both without adding Platform.isAndroid / Platform.isIOS branches around the
download manager constructor:
final manager = DefaultModelDownloadManager.auto(
androidAppPrivateCacheDirectory: androidModelsDirectory,
iosAppPrivateCacheDirectory: iosModelsDirectory,
);To preserve the old temporary-cache behavior exactly on desktop/server, pass an explicit directory:
final manager = DefaultModelDownloadManager(
defaultCacheDirectory: path.join(
Directory.systemTemp.path,
'llamadart',
'models',
),
);If your application previously called DefaultModelDownloadManager.auto() on
Android/iOS and expected a LlamaUnsupportedException, update that test or call
DefaultModelDownloadManager.sharedCache() without cacheDirectory when you
specifically want to reject implicit mobile shared caches.
No public API break, but Android arm64 native packaging defaults changed.
- Shorthand config such as
android-arm64: [vulkan]is still supported. - If no CPU policy is set, Android arm64 now defaults to
cpu_profile: full(all CPU variants). - If you want smaller baseline-only packaging, set
cpu_profile: compactexplicitly. cpu_variants: [...](when provided) overridescpu_profile.
Example (preserve compact baseline-style packaging):
hooks:
user_defines:
llamadart:
llamadart_native_backends:
platforms:
android-arm64:
backends: [vulkan]
cpu_profile: compactThe legacy custom handler/override registry APIs were removed:
ChatTemplateEngine.registerHandler(...)ChatTemplateEngine.unregisterHandler(...)ChatTemplateEngine.clearCustomHandlers(...)ChatTemplateEngine.registerTemplateOverride(...)ChatTemplateEngine.unregisterTemplateOverride(...)ChatTemplateEngine.clearTemplateOverrides(...)
Legacy per-call handler routing fields were also removed:
- render param:
customHandlerId - parse param:
handlerId
Template render/parse paths no longer silently downgrade to content-only fallback when a handler/parser fails. Failures are now surfaced to the caller.
Audit call sites that previously relied on silent fallback behavior and handle exceptions explicitly.
- Old pattern (string-in, string-out helpers):
session.chat(...)session.chatText(...)
- New pattern:
session.create(List<LlamaContentPart> ...)- stream
LlamaCompletionChunk
Example migration:
// Before
await for (final token in session.chat('Hello')) {
stdout.write(token);
}
// After
await for (final chunk in session.create([LlamaTextContent('Hello')])) {
stdout.write(chunk.choices.first.delta.content ?? '');
}LlamaChatMessage.text(...)->LlamaChatMessage.fromText(...)LlamaChatMessage.multimodal(...)->LlamaChatMessage.withContent(...)
Example migration:
// Before
LlamaChatMessage.text(role: LlamaChatRole.user, content: 'Hi');
// After
LlamaChatMessage.fromText(role: LlamaChatRole.user, text: 'Hi');- Removed:
ModelParams(logLevel: ...) - Use
LlamaLogging.configureinstead:level:for Dart-side logs andnativeLevel:for native logs (by default the same aslevel:)
Example migration:
// Before
await engine.loadModel(path, modelParams: ModelParams(logLevel: LlamaLogLevel.info));
// After
await LlamaLogging.configure(nativeLevel: LlamaLogLevel.info);
await engine.loadModel(path);loadModel(...)now throws if a model is already loaded.- Call
await engine.unloadModel()before loading another model into the same engine.dispose()is terminal: a disposed engine cannot load again, so create a newLlamaEngineafter it. Current releases replace the loaded model withsetModelinstead.
The package root (package:llamadart/llamadart.dart) no longer exports some
previous internals. In particular:
ToolRegistryLlamaTokenizerChatTemplateProcessor
Use LlamaEngine, ChatSession, ToolDefinition, and the template APIs as
the supported surface.
If you maintain your own LlamaBackend implementation, update it to match the
current interface:
- Add
getVramInfo(). - Update
applyChatTemplate(...)signature/return type (string-based prompt rendering input/output).
Template/render/parse behavior is now strict llama.cpp parity:
customTemplateremains supported for per-call template overrides.- Legacy
customHandlerId/parsehandlerIdrouting was removed. ChatTemplateEngine.registerHandler(...)andChatTemplateEngine.registerTemplateOverride(...)were removed.- Render/parse paths no longer silently downgrade to content-only fallback when a handler/parser fails; failures are surfaced to the caller.
- Replace old
ChatSessionchat helpers withcreate(...)streaming. - Rename
LlamaChatMessagenamed constructors. - Remove
ModelParams.logLevelusage. - Audit imports that depended on removed root exports.
- For custom backends, implement the latest
LlamaBackendinterface.