initialize static method

Future<RagEngine> initialize({
  1. required RagConfig config,
  2. void onProgress(
    1. String status
    )?,
})

Initialize RagEngine with all dependencies.

This method handles:

  1. Copying tokenizer asset to documents directory
  2. Initializing the tokenizer
  3. Loading the ONNX embedding model
  4. Initializing the RAG database

config - Configuration containing asset paths and options. onProgress - Optional callback for initialization status updates.

Example:

final rag = await RagEngine.initialize(
  config: RagConfig.fromAssets(
    tokenizerAsset: 'assets/tokenizer.json',
    modelAsset: 'assets/model.onnx',
  ),
  onProgress: (status) => setState(() => _status = status),
);

Implementation

static Future<RagEngine> initialize({
  required RagConfig config,
  void Function(String status)? onProgress,
}) async {
  // 0. Auto-initialize Rust library (safe to call multiple times)
  await _ensureRustInitialized();

  // 1. Get app documents directory
  final dir = await getApplicationDocumentsDirectory();
  final dbPath = "${dir.path}/${config.databaseName ?? 'rag.sqlite'}";
  final tokenizerPath = "${dir.path}/tokenizer.json";
  final modelPath = "${dir.path}/${config.modelAsset.split('/').last}";

  // 2. Copy and initialize tokenizer
  onProgress?.call('Initializing tokenizer...');
  await _copyAssetToFile(config.tokenizerAsset, tokenizerPath);
  await initTokenizer(tokenizerPath: tokenizerPath);
  final vocabSize = getVocabSize();

  // 3. Prepare ONNX embedding model (Copy logic)
  onProgress?.call('Preparing embedding model...');
  // Copy model asset to file (optimized for memory)
  await _copyAssetToFile(config.modelAsset, modelPath);

  // Configure session options if thread limit is requested
  OrtSessionOptions? sessionOptions;

  // Default to half the cores if not specified to prevent full CPU usage
  // Calculate threads based on configuration
  int threads;
  final totalCores = Platform.numberOfProcessors;

  if (config.threadLevel != null) {
    // 1. Thread Level (Percentage based)
    switch (config.threadLevel!) {
      case ThreadUseLevel.low:
        threads = (totalCores * 0.2).ceil();
        break;
      case ThreadUseLevel.medium:
        threads = (totalCores * 0.4).ceil();
        break;
      case ThreadUseLevel.high:
        threads = (totalCores * 0.8).ceil();
        break;
    }
  } else if (config.embeddingIntraOpNumThreads != null) {
    // 2. Manual Count
    threads = config.embeddingIntraOpNumThreads!;
  } else {
    // 3. Priority: Default (50% safe fallback)
    threads = (totalCores > 1 ? (totalCores / 2).ceil() : 1);
  }

  // Ensure at least 1 thread
  if (threads < 1) threads = 1;

  // Apply thread configuration
  sessionOptions = OrtSessionOptions();
  try {
    sessionOptions.setIntraOpNumThreads(threads);
    debugPrint(
      '[RagEngine] Configured ONNX embedding threads: $threads (Total Cores: $totalCores)',
    );
  } catch (e) {
    debugPrint('[RagEngine] Warning: Failed to set intra-op num threads: $e');
  }

  // Init EmbeddingService with file path
  onProgress?.call('Loading embedding model...');
  await EmbeddingService.init(modelPath: modelPath, options: sessionOptions);

  // 4. Initialize database connection pool
  onProgress?.call('Initializing connection pool...');
  await initDbPool(dbPath: dbPath, maxSize: 4);

  // 5. Initialize RAG service
  onProgress?.call('Initializing database...');
  final ragService = SourceRagService(
    dbPath: dbPath,
    modelPath: modelPath,
    maxChunkChars: config.maxChunkChars,
    overlapChars: config.overlapChars,
  );
  await ragService.init(deferIndexWarmup: config.deferIndexWarmup);

  onProgress?.call('Ready!');
  return RagEngine._(
    ragService: ragService,
    dbPath: dbPath,
    vocabSize: vocabSize,
    deferIndexWarmup: config.deferIndexWarmup,
  );
}