initialize method

Future<void> initialize({
  1. required String modelPath,
  2. String backend = 'gpu',
  3. int maxTokens = 2048,
  4. String? cacheDir,
  5. bool enableVision = false,
  6. int maxNumImages = 0,
  7. bool enableAudio = false,
})

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);
  }
}