createDecider method

  1. @experimental
  2. @nonVirtual
Future<Decider> createDecider({
  1. required List<DeciderOption> options,
  2. DeciderMethod method = DeciderMethod.centroid,
  3. EmbeddingDeciderCalibration calibration = const EmbeddingDeciderCalibration.crossFit(),
  4. String? embedderId,
  5. CancelToken? cancelToken,
  6. void onProgress(
    1. int done,
    2. int total
    )?,
})

A decider that picks one of options for an input, or abstains.

Every option's inputs — its description, then its examples — are embedded once, here, with TaskType.sentenceSimilarity (the prefix every compared input gets), and fitted by method: centroids, plus the linear layer for DeciderMethod.linear. calibration sets when the decider abstains; the default cross-fit needs two inputs per option. A choose then costs one embed and a dot product per option.

The decider borrows this model and never closes it. When a model FlutterEdgeAi.getActiveEmbedder returned is closed — core replaced it (other media, another backend, a re-install), its worker died, or the app closed it — the decider's next choose asks core for the active embedder again, with the backend and media this model was built with (so it may load the model again), and retries there when that embedder's space id is the decider's; another space fails with DeciderSpaceMismatchException. On any other model choose fails with EmbedderClosedException and the decider reports isClosed: reload it with loadDecider from its toArtifact() — no re-embedding.

embedderId names the model's weights in the decider's embedding-space id. A model FlutterEdgeAi.getActiveEmbedder returned is named by its installed files unless one is given: their names and, natively, their sizes in bytes — file names and sizes, not the bytes themselves (on web the names alone). Any other model (a test fake, a custom subclass) needs an embedderId, and so does a model whose activeBackend is null (LiteRT.js and ONNX on web): without a backend to tell WebGPU from WASM, the call is refused with ArgumentError. An embedderId REPLACES the installed-file guard: the caller owns the identity, and with a null activeBackend it must name the backend too. The space id also holds the dimension, activeBackend and the task prefix, so a decider fitted on the CPU file is never loaded onto an NPU bundle's vectors, and — when an option example or a labelled request is an image — the engine's image budget (EmbeddingGemma 2: 140 or 70 tokens per image), so image vectors of one budget never meet the other's.

cancelToken stops the embedding of the options and the fit (natively the fit's isolate is killed; on web the fit stops at its next yield); the call then fails with DeciderCancelledException and returns no decider.

onProgress counts the inputs this call embeds — each option's description and examples, then the rows of EmbeddingDeciderCalibration.fromLabelled — as (done, total): (0, total) first, then once per input as its vector comes back, ending at (total, total). It does not cover the fit that follows: a DeciderMethod.linear fit at 150 options takes seconds after the last call. A throw from it fails the call.

Errors arrive as a FAILED FUTURE: ArgumentError for options it cannot use (none, duplicate ids, an option with nothing to embed, a calibration these options or labelled rows cannot support) and for DeciderMethod.optionScoring, an InferenceModel method; StateError for a vector that is not finite or not dimension long and for a fit that is not finite (an option whose inputs cancel out); and this model's own failures — an over-long input is EmbeddingInputTooLongException, never truncated; an input's failure names its option and input, with its type and fields kept.

Experimental: may change in a minor release while the API settles.

Implementation

@experimental
@nonVirtual
Future<Decider> createDecider({
  required List<DeciderOption> options,
  DeciderMethod method = DeciderMethod.centroid,
  EmbeddingDeciderCalibration calibration =
      const EmbeddingDeciderCalibration.crossFit(),
  String? embedderId,
  CancelToken? cancelToken,
  void Function(int done, int total)? onProgress,
}) => createEmbeddingDecider(
  this,
  options: options,
  method: method,
  calibration: calibration,
  embedderId: embedderId,
  cancelToken: cancelToken,
  onProgress: onProgress,
);