initialize method
Initialize the engine with model path and settings.
Implementation
Future<void> initialize({
required String modelPath,
String backend = 'gpu',
int maxTokens = 2048,
String? cacheDir,
bool enableVision = false,
int maxNumImages = 0,
bool enableAudio = false,
}) async {
final initSw = Stopwatch()..start();
_ensureBindings();
final bindingsMs = initSw.elapsedMilliseconds;
debugPrint('[LiteRtLmFfi/perf] _ensureBindings: ${bindingsMs}ms');
final b = _bindings!;
// Create engine settings
final modelPathPtr = modelPath.toNativeUtf8();
final backendPtr = backend.toNativeUtf8();
final visionBackendPtr = enableVision ? backend.toNativeUtf8() : nullptr;
final audioBackendPtr = enableAudio ? 'cpu'.toNativeUtf8() : nullptr;
try {
final settingsCreateStart = initSw.elapsedMilliseconds;
final settings = b.litert_lm_engine_settings_create(
modelPathPtr.cast(),
backendPtr.cast(),
visionBackendPtr == nullptr ? nullptr : visionBackendPtr.cast(),
audioBackendPtr == nullptr ? nullptr : audioBackendPtr.cast(),
);
debugPrint(
'[LiteRtLmFfi/perf] settings_create: ${initSw.elapsedMilliseconds - settingsCreateStart}ms');
if (settings == nullptr) {
throw Exception('Failed to create engine settings');
}
// Configure settings
b.litert_lm_engine_settings_set_max_num_tokens(settings, maxTokens);
if (cacheDir != null) {
final cacheDirPtr = cacheDir.toNativeUtf8();
// Sets cache dir on main, vision, and audio executors (C API patched)
b.litert_lm_engine_settings_set_cache_dir(settings, cacheDirPtr.cast());
calloc.free(cacheDirPtr);
}
if (maxNumImages > 0) {
b.litert_lm_engine_settings_set_max_num_images(settings, maxNumImages);
}
// Create engine in a background isolate to avoid blocking UI.
// Pass settings pointer as int address (Pointer can't cross isolates).
debugPrint(
'[LiteRtLmFfi] Creating engine from $modelPath (backend=$backend, maxTokens=$maxTokens) ...');
debugPrint(
'[LiteRtLmFfi/perf] === START litert_lm_engine_create (native — model load + accelerator init + KV cache prefill) ===');
final settingsAddr = settings.address;
final sw = Stopwatch()..start();
final engineAddr = await Isolate.run(() {
final isolateSw = Stopwatch()..start();
final lib = Platform.isIOS
? DynamicLibrary.open(
'@executable_path/Frameworks/LiteRtLm.framework/LiteRtLm')
: Platform.isMacOS
? DynamicLibrary.open('LiteRtLm.framework/LiteRtLm')
: (Platform.isLinux || Platform.isAndroid)
? DynamicLibrary.open('libLiteRtLm.so')
: DynamicLibrary.open('LiteRtLm.dll');
// ignore: avoid_print
print(
'[LiteRtLmFfi/perf] isolate: DynamicLibrary.open: ${isolateSw.elapsedMilliseconds}ms');
final lookupStart = isolateSw.elapsedMilliseconds;
final create = lib.lookupFunction<Pointer Function(Pointer),
Pointer Function(Pointer)>('litert_lm_engine_create');
// ignore: avoid_print
print(
'[LiteRtLmFfi/perf] isolate: lookupFunction: ${isolateSw.elapsedMilliseconds - lookupStart}ms');
final createStart = isolateSw.elapsedMilliseconds;
final ptr = create(Pointer.fromAddress(settingsAddr)).address;
// ignore: avoid_print
print(
'[LiteRtLmFfi/perf] isolate: native litert_lm_engine_create: ${isolateSw.elapsedMilliseconds - createStart}ms');
return ptr;
});
_engine = Pointer<LiteRtLmEngine>.fromAddress(engineAddr);
sw.stop();
debugPrint(
'[LiteRtLmFfi/perf] === END litert_lm_engine_create: ${sw.elapsedMilliseconds}ms (includes isolate spawn ~50-200ms) ===');
debugPrint(
'[LiteRtLmFfi] litert_lm_engine_create took ${sw.elapsedMilliseconds}ms');
b.litert_lm_engine_settings_delete(settings);
if (_engine == null || _engine == nullptr) {
_dumpNativeLog();
throw Exception(
'Failed to create engine. Model may be invalid: $modelPath');
}
_isInitialized = true;
debugPrint(
'[LiteRtLmFfi/perf] initialize() total: ${initSw.elapsedMilliseconds}ms');
debugPrint('[LiteRtLmFfi] Engine initialized successfully');
// Auto-dump the SDK's stderr log after successful engine_create so
// users can see what happens inside the native call (model load time,
// accelerator init, sampler dlopen attempts, KV cache prefill, etc.).
// No-op when stderr redirection isn't wired (release / Android /
// Windows). Safe to call before _isInitialized was true since the
// dump only reads a file, doesn't touch native state.
_dumpNativeLog();
} finally {
calloc.free(modelPathPtr);
calloc.free(backendPtr);
if (visionBackendPtr != nullptr) calloc.free(visionBackendPtr);
if (audioBackendPtr != nullptr) calloc.free(audioBackendPtr);
}
}