createEmbeddingModel method
Future<EmbeddingModel>
createEmbeddingModel({
- String? modelPath,
- String? tokenizerPath,
- PreferredBackend? preferredBackend,
override
Creates and returns a new EmbeddingModel instance.
Modern API: If paths are not provided, uses the active embedding model set via
FlutterGemma.installEmbedder() or modelManager.setActiveModel().
Legacy API: Provide explicit paths for backward compatibility.
modelPath — path to the embedding model file (optional if active model set).
tokenizerPath — path to the tokenizer file (optional if active model set).
preferredBackend — backend preference (e.g., CPU, GPU).
Implementation
@override
Future<EmbeddingModel> createEmbeddingModel({
String? modelPath,
String? tokenizerPath,
PreferredBackend? preferredBackend,
}) async {
// Modern API: Use active embedding model if paths not provided
if (modelPath == null || tokenizerPath == null) {
final manager = modelManager as WebModelManager;
final activeModel = manager.activeEmbeddingModel;
// No active embedding model - user must set one first
if (activeModel == null) {
throw StateError(
'No active embedding model set. Use `FlutterGemma.installEmbedder()` or `modelManager.setActiveModel()` to set a model first');
}
// Get the actual model file paths through unified system
final modelFilePaths = await manager.getModelFilePaths(activeModel);
if (modelFilePaths == null || modelFilePaths.isEmpty) {
throw StateError(
'Embedding model file paths not found. Use the `modelManager` to load the model first');
}
// Extract model and tokenizer paths from spec
final activeModelPath =
modelFilePaths[PreferencesKeys.embeddingModelFile];
final activeTokenizerPath =
modelFilePaths[PreferencesKeys.embeddingTokenizerFile];
if (activeModelPath == null || activeTokenizerPath == null) {
throw StateError(
'Could not find model or tokenizer path in active embedding model');
}
modelPath = activeModelPath;
tokenizerPath = activeTokenizerPath;
if (kDebugMode) {
gemmaLog(
'Using active embedding model: $modelPath, tokenizer: $tokenizerPath');
}
}
// Check if model already exists with different parameters. The LiteRT.js
// embedding runtime now lives in flutter_gemma_embeddings, so core can no
// longer downcast to the package's WebEmbeddingModel to read its paths —
// it compares against the last resolved paths it cached itself.
if (_initializedEmbeddingModel != null) {
final p = _lastEmbeddingPaths;
final modelChanged = p == null ||
p.modelPath != modelPath ||
p.tokenizerPath != tokenizerPath;
if (modelChanged) {
if (kDebugMode) {
gemmaLog(
'[FlutterGemmaWeb] Embedding model paths changed, closing existing model');
}
await _initializedEmbeddingModel?.close();
_initializedEmbeddingModel = null;
_lastEmbeddingPaths = null;
}
}
if (_initializedEmbeddingModel != null) {
return _initializedEmbeddingModel!;
}
// The LiteRT.js embedding runtime moved to flutter_gemma_embeddings; core
// resolves paths (preamble above) + owns the singleton lifecycle, then
// dispatches construction through the EmbeddingRegistry. The backend reads
// ONLY config.modelPath/config.tokenizerPath — it ignores the spec for path
// resolution. Web selects by the sole registered backend (WebGPU LiteRT.js).
final activeEmb = (modelManager as WebModelManager).activeEmbeddingModel;
final EmbeddingBackendProvider? backend = activeEmb is EmbeddingModelSpec
? EmbeddingRegistry.instance.findFor(activeEmb)
: (EmbeddingRegistry.instance.registered.isNotEmpty
? EmbeddingRegistry.instance.registered.first
: null);
if (backend == null) {
throw StateError(
'No embedding backend registered. Add flutter_gemma_embeddings to '
'pubspec.yaml and pass it in embeddingBackends: of '
'FlutterGemma.initialize(...). Registered backends: '
'${EmbeddingRegistry.instance.registered.map((b) => b.name).join(", ")}.',
);
}
// modelPath/tokenizerPath are non-null here (resolved in the preamble or
// passed by the caller). preferredBackend is ignored on web (LiteRT.js uses
// WebGPU when available); maxTokens is unused by embeddings.
final embConfig = RuntimeConfig(
maxTokens: 0,
modelPath: modelPath,
tokenizerPath: tokenizerPath,
preferredBackend: preferredBackend,
);
// The backend's createModel(spec, config) requires a non-null spec but
// resolves paths exclusively from config; synthesize one from the resolved
// file paths when there's no active EmbeddingModelSpec.
final model = await backend.createModel(
activeEmb is EmbeddingModelSpec
? activeEmb
: EmbeddingModelSpec(
name: 'web-active-embedding',
modelSource: ModelSource.file(modelPath),
tokenizerSource: ModelSource.file(tokenizerPath),
),
embConfig,
);
_initializedEmbeddingModel = model;
_lastEmbeddingPaths = (modelPath: modelPath, tokenizerPath: tokenizerPath);
model.addCloseListener(() {
_initializedEmbeddingModel = null;
_lastEmbeddingPaths = null;
});
return model;
}