use std::{
num::NonZeroU32,
path::{Path, PathBuf},
};
use crate::{
ComputeUnits, LoadOptions,
embeddings::{aggregate::AggregatePolicyKind, long_text::LongTextOptions},
};
use super::{
artifacts::ArtifactLabel,
audio::AudioPrecision,
config::MAX_TOKENS,
error::{BudgetNotLoaded, Error, Result, WindowOverBudget},
placement::{PACKED_SLOT_INVARIANT, PlacementTable},
text::TextFunctions,
tokenizer::SplitSpecialTokens,
vision::{ResizeRoad, VisionBudget, VisionBudgets},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::Display)]
#[display("{}", self.as_str())]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum Batching {
Packed,
Single,
}
impl Batching {
#[inline]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Packed => "packed",
Self::Single => "single",
}
}
}
pub const DEFAULT_TEXT_COREML: LoadOptions = LoadOptions::new(ComputeUnits::CpuAndNeuralEngine);
pub const DEFAULT_AUDIO_COREML: LoadOptions = LoadOptions::new(ComputeUnits::CpuAndGpu);
pub const DEFAULT_VISION_COREML: LoadOptions = LoadOptions::new(ComputeUnits::CpuAndGpu);
pub const DEFAULT_TEXT_BATCHING: Batching = Batching::Packed;
pub const DEFAULT_AUDIO_BATCHING: Batching = Batching::Single;
pub const DEFAULT_VISION_BATCHING: Batching = Batching::Single;
pub const DEFAULT_SPLIT_SPECIAL_TOKENS: bool = false;
pub const DEFAULT_FOLD: AggregatePolicyKind = AggregatePolicyKind::CoverageWeightedMean;
pub const DEFAULT_AUDIO_PRECISION: AudioPrecision = AudioPrecision::Fp16;
pub const DEFAULT_HOP_SAMPLES: NonZeroU32 = NonZeroU32::new(160_000).unwrap();
pub const DEFAULT_MIN_TAIL_SAMPLES: u32 = 8_000;
pub const DEFAULT_BUDGET: VisionBudget = VisionBudget::Tokens140;
pub const DEFAULT_BUDGETS: VisionBudgets = VisionBudgets::only(VisionBudget::Tokens140);
pub const DEFAULT_RESIZE: ResizeRoad = ResizeRoad::HfBicubic;
#[cfg(feature = "serde")]
mod serde_defaults {
use super::*;
pub(super) fn text_coreml() -> LoadOptions {
DEFAULT_TEXT_COREML
}
pub(super) fn audio_coreml() -> LoadOptions {
DEFAULT_AUDIO_COREML
}
pub(super) fn vision_coreml() -> LoadOptions {
DEFAULT_VISION_COREML
}
pub(super) fn text_batching() -> Batching {
DEFAULT_TEXT_BATCHING
}
pub(super) fn audio_batching() -> Batching {
DEFAULT_AUDIO_BATCHING
}
pub(super) fn vision_batching() -> Batching {
DEFAULT_VISION_BATCHING
}
pub(super) fn split_special_tokens() -> bool {
DEFAULT_SPLIT_SPECIAL_TOKENS
}
pub(super) fn fold() -> AggregatePolicyKind {
DEFAULT_FOLD
}
pub(super) fn audio_precision() -> AudioPrecision {
DEFAULT_AUDIO_PRECISION
}
pub(super) fn hop_samples() -> NonZeroU32 {
DEFAULT_HOP_SAMPLES
}
pub(super) fn min_tail_samples() -> u32 {
DEFAULT_MIN_TAIL_SAMPLES
}
pub(super) fn budgets() -> VisionBudgets {
DEFAULT_BUDGETS
}
pub(super) fn budget() -> VisionBudget {
DEFAULT_BUDGET
}
pub(super) fn resize() -> ResizeRoad {
DEFAULT_RESIZE
}
}
struct DirTerm<'a>(Option<&'a Path>);
impl core::fmt::Display for DirTerm<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.0 {
Some(dir) => write!(f, "{dir:?}"),
None => f.write_str("none"),
}
}
}
struct OptionTerm<T>(Option<T>);
impl<T: core::fmt::Display> core::fmt::Display for OptionTerm<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match &self.0 {
Some(value) => write!(f, "{value}"),
None => f.write_str("none"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct TextOptions {
#[cfg_attr(feature = "serde", serde(default))]
dir: Option<PathBuf>,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::text_coreml"))]
coreml: LoadOptions,
#[cfg_attr(feature = "serde", serde(default))]
prewarm: Option<TextFunctions>,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::text_batching"))]
batching: Batching,
#[cfg_attr(
feature = "serde",
serde(default = "serde_defaults::split_special_tokens")
)]
split_special_tokens: bool,
#[cfg_attr(feature = "serde", serde(default))]
long_text: LongTextOptions,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::fold"))]
fold: AggregatePolicyKind,
#[cfg_attr(feature = "serde", serde(default))]
expect_artifacts: Option<ArtifactLabel>,
}
impl Default for TextOptions {
fn default() -> Self {
Self::new()
}
}
impl TextOptions {
pub const fn new() -> Self {
Self {
dir: None,
coreml: DEFAULT_TEXT_COREML,
prewarm: None,
batching: DEFAULT_TEXT_BATCHING,
split_special_tokens: DEFAULT_SPLIT_SPECIAL_TOKENS,
long_text: LongTextOptions::new(),
fold: DEFAULT_FOLD,
expect_artifacts: None,
}
}
#[inline]
pub fn dir(&self) -> Option<&Path> {
self.dir.as_deref()
}
#[must_use]
pub fn with_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.set_dir(Some(dir.into()));
self
}
pub fn set_dir(&mut self, dir: Option<PathBuf>) -> &mut Self {
self.dir = dir;
self
}
#[inline]
pub const fn coreml(&self) -> LoadOptions {
self.coreml
}
#[must_use]
pub fn with_coreml(mut self, coreml: LoadOptions) -> Self {
self.set_coreml(coreml);
self
}
pub fn set_coreml(&mut self, coreml: LoadOptions) -> &mut Self {
self.coreml = coreml;
self
}
#[inline]
pub const fn prewarm(&self) -> Option<TextFunctions> {
self.prewarm
}
#[must_use]
pub fn with_prewarm(mut self, functions: TextFunctions) -> Self {
self.set_prewarm(Some(functions));
self
}
pub fn set_prewarm(&mut self, functions: Option<TextFunctions>) -> &mut Self {
self.prewarm = functions;
self
}
#[inline]
pub const fn batching(&self) -> Batching {
self.batching
}
#[inline]
pub const fn batch_independent(&self) -> bool {
self.batching.batch_independent(self.coreml.compute())
}
#[must_use]
pub fn with_batching(mut self, batching: Batching) -> Self {
self.set_batching(batching);
self
}
pub fn set_batching(&mut self, batching: Batching) -> &mut Self {
self.batching = batching;
self
}
#[inline]
pub const fn split_special_tokens(&self) -> bool {
self.split_special_tokens
}
#[inline]
pub const fn split(&self) -> SplitSpecialTokens {
SplitSpecialTokens::from_split(self.split_special_tokens)
}
#[must_use]
pub fn with_split_special_tokens(mut self, split: bool) -> Self {
self.set_split_special_tokens(split);
self
}
pub fn set_split_special_tokens(&mut self, split: bool) -> &mut Self {
self.split_special_tokens = split;
self
}
#[inline]
pub const fn long_text(&self) -> LongTextOptions {
self.long_text
}
#[must_use]
pub fn with_long_text(mut self, long_text: LongTextOptions) -> Self {
self.set_long_text(long_text);
self
}
pub fn set_long_text(&mut self, long_text: LongTextOptions) -> &mut Self {
self.long_text = long_text;
self
}
#[inline]
pub const fn fold(&self) -> AggregatePolicyKind {
self.fold
}
#[must_use]
pub fn with_fold(mut self, fold: AggregatePolicyKind) -> Self {
self.set_fold(fold);
self
}
pub fn set_fold(&mut self, fold: AggregatePolicyKind) -> &mut Self {
self.fold = fold;
self
}
#[inline]
pub const fn expect_artifacts(&self) -> Option<ArtifactLabel> {
self.expect_artifacts
}
#[must_use]
pub fn with_expect_artifacts(mut self, label: ArtifactLabel) -> Self {
self.set_expect_artifacts(Some(label));
self
}
pub fn set_expect_artifacts(&mut self, label: Option<ArtifactLabel>) -> &mut Self {
self.expect_artifacts = label;
self
}
pub fn validate(&self) -> Result<()> {
let geometry = self.long_text.window_options();
if geometry.window() > MAX_TOKENS {
return Err(Error::WindowOverBudget(WindowOverBudget::new(
geometry.window(),
MAX_TOKENS,
)));
}
geometry.validate()?;
Ok(())
}
}
impl core::fmt::Display for TextOptions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"dir={},coreml=({}),prewarm=",
DirTerm(self.dir()),
self.coreml
)?;
match self.prewarm {
Some(functions) => write!(f, "{functions}")?,
None => f.write_str("reachable")?,
}
write!(
f,
",batching={},split_special_tokens={},long_text=({}),fold={},expect_artifacts={}",
self.batching,
self.split_special_tokens,
self.long_text,
self.fold,
OptionTerm(self.expect_artifacts)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct AudioOptions {
#[cfg_attr(feature = "serde", serde(default))]
dir: Option<PathBuf>,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::audio_precision"))]
precision: AudioPrecision,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::audio_coreml"))]
coreml: LoadOptions,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::audio_batching"))]
batching: Batching,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::hop_samples"))]
hop_samples: NonZeroU32,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::min_tail_samples"))]
min_tail_samples: u32,
#[cfg_attr(feature = "serde", serde(default))]
max_windows: Option<NonZeroU32>,
#[cfg_attr(feature = "serde", serde(default))]
expect_artifacts: Option<ArtifactLabel>,
}
impl Default for AudioOptions {
fn default() -> Self {
Self::new()
}
}
impl AudioOptions {
pub const fn new() -> Self {
Self {
dir: None,
precision: DEFAULT_AUDIO_PRECISION,
coreml: DEFAULT_AUDIO_COREML,
batching: DEFAULT_AUDIO_BATCHING,
hop_samples: DEFAULT_HOP_SAMPLES,
min_tail_samples: DEFAULT_MIN_TAIL_SAMPLES,
max_windows: None,
expect_artifacts: None,
}
}
#[inline]
pub fn dir(&self) -> Option<&Path> {
self.dir.as_deref()
}
#[must_use]
pub fn with_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.set_dir(Some(dir.into()));
self
}
pub fn set_dir(&mut self, dir: Option<PathBuf>) -> &mut Self {
self.dir = dir;
self
}
#[inline]
pub const fn precision(&self) -> AudioPrecision {
self.precision
}
#[must_use]
pub fn with_precision(mut self, precision: AudioPrecision) -> Self {
self.set_precision(precision);
self
}
pub fn set_precision(&mut self, precision: AudioPrecision) -> &mut Self {
self.precision = precision;
self
}
#[inline]
pub const fn coreml(&self) -> LoadOptions {
self.coreml
}
#[must_use]
pub fn with_coreml(mut self, coreml: LoadOptions) -> Self {
self.set_coreml(coreml);
self
}
pub fn set_coreml(&mut self, coreml: LoadOptions) -> &mut Self {
self.coreml = coreml;
self
}
#[inline]
pub const fn batching(&self) -> Batching {
self.batching
}
#[must_use]
pub fn with_batching(mut self, batching: Batching) -> Self {
self.set_batching(batching);
self
}
pub fn set_batching(&mut self, batching: Batching) -> &mut Self {
self.batching = batching;
self
}
#[inline]
pub const fn hop_samples(&self) -> NonZeroU32 {
self.hop_samples
}
#[must_use]
pub fn with_hop_samples(mut self, hop: NonZeroU32) -> Self {
self.set_hop_samples(hop);
self
}
pub fn set_hop_samples(&mut self, hop: NonZeroU32) -> &mut Self {
self.hop_samples = hop;
self
}
#[inline]
pub const fn min_tail_samples(&self) -> u32 {
self.min_tail_samples
}
#[must_use]
pub fn with_min_tail_samples(mut self, samples: u32) -> Self {
self.set_min_tail_samples(samples);
self
}
pub fn set_min_tail_samples(&mut self, samples: u32) -> &mut Self {
self.min_tail_samples = samples;
self
}
#[inline]
pub const fn max_windows(&self) -> Option<NonZeroU32> {
self.max_windows
}
#[must_use]
pub fn with_max_windows(mut self, max: NonZeroU32) -> Self {
self.set_max_windows(Some(max));
self
}
pub fn set_max_windows(&mut self, max: Option<NonZeroU32>) -> &mut Self {
self.max_windows = max;
self
}
#[inline]
pub const fn expect_artifacts(&self) -> Option<ArtifactLabel> {
self.expect_artifacts
}
#[must_use]
pub fn with_expect_artifacts(mut self, label: ArtifactLabel) -> Self {
self.set_expect_artifacts(Some(label));
self
}
pub fn set_expect_artifacts(&mut self, label: Option<ArtifactLabel>) -> &mut Self {
self.expect_artifacts = label;
self
}
}
impl core::fmt::Display for AudioOptions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"dir={},precision={},coreml=({}),batching={},hop_samples={},min_tail_samples={},max_windows={},expect_artifacts={}",
DirTerm(self.dir()),
self.precision,
self.coreml,
self.batching,
self.hop_samples,
self.min_tail_samples,
OptionTerm(self.max_windows),
OptionTerm(self.expect_artifacts)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct ImageOptions {
#[cfg_attr(feature = "serde", serde(default))]
dir: Option<PathBuf>,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::vision_coreml"))]
coreml: LoadOptions,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::vision_batching"))]
batching: Batching,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::budgets"))]
budgets: VisionBudgets,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::budget"))]
default_budget: VisionBudget,
#[cfg_attr(feature = "serde", serde(default = "serde_defaults::resize"))]
resize: ResizeRoad,
#[cfg_attr(feature = "serde", serde(default))]
expect_artifacts: Option<ArtifactLabel>,
}
impl Default for ImageOptions {
fn default() -> Self {
Self::new()
}
}
impl ImageOptions {
pub const fn new() -> Self {
Self {
dir: None,
coreml: DEFAULT_VISION_COREML,
batching: DEFAULT_VISION_BATCHING,
budgets: DEFAULT_BUDGETS,
default_budget: DEFAULT_BUDGET,
resize: DEFAULT_RESIZE,
expect_artifacts: None,
}
}
#[inline]
pub fn dir(&self) -> Option<&Path> {
self.dir.as_deref()
}
#[must_use]
pub fn with_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.set_dir(Some(dir.into()));
self
}
pub fn set_dir(&mut self, dir: Option<PathBuf>) -> &mut Self {
self.dir = dir;
self
}
#[inline]
pub const fn coreml(&self) -> LoadOptions {
self.coreml
}
#[must_use]
pub fn with_coreml(mut self, coreml: LoadOptions) -> Self {
self.set_coreml(coreml);
self
}
pub fn set_coreml(&mut self, coreml: LoadOptions) -> &mut Self {
self.coreml = coreml;
self
}
#[inline]
pub const fn batching(&self) -> Batching {
self.batching
}
#[must_use]
pub fn with_batching(mut self, batching: Batching) -> Self {
self.set_batching(batching);
self
}
pub fn set_batching(&mut self, batching: Batching) -> &mut Self {
self.batching = batching;
self
}
#[inline]
pub const fn budgets(&self) -> VisionBudgets {
self.budgets
}
#[must_use]
pub fn with_budgets(mut self, budgets: VisionBudgets) -> Self {
self.set_budgets(budgets);
self
}
pub fn set_budgets(&mut self, budgets: VisionBudgets) -> &mut Self {
self.budgets = budgets;
self
}
#[inline]
pub const fn default_budget(&self) -> VisionBudget {
self.default_budget
}
#[must_use]
pub fn with_default_budget(mut self, budget: VisionBudget) -> Self {
self.set_default_budget(budget);
self
}
pub fn set_default_budget(&mut self, budget: VisionBudget) -> &mut Self {
self.default_budget = budget;
self
}
#[inline]
pub const fn resize(&self) -> ResizeRoad {
self.resize
}
#[must_use]
pub fn with_resize(mut self, resize: ResizeRoad) -> Self {
self.set_resize(resize);
self
}
pub fn set_resize(&mut self, resize: ResizeRoad) -> &mut Self {
self.resize = resize;
self
}
#[inline]
pub const fn expect_artifacts(&self) -> Option<ArtifactLabel> {
self.expect_artifacts
}
#[must_use]
pub fn with_expect_artifacts(mut self, label: ArtifactLabel) -> Self {
self.set_expect_artifacts(Some(label));
self
}
pub fn set_expect_artifacts(&mut self, label: Option<ArtifactLabel>) -> &mut Self {
self.expect_artifacts = label;
self
}
pub fn validate(&self) -> Result<()> {
if self.budgets.contains(self.default_budget) {
Ok(())
} else {
Err(Error::BudgetNotLoaded(BudgetNotLoaded::new(
self.default_budget,
self.budgets,
)))
}
}
}
impl core::fmt::Display for ImageOptions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"dir={},coreml=({}),batching={},budgets={},default_budget={},resize={},expect_artifacts={}",
DirTerm(self.dir()),
self.coreml,
self.batching,
self.budgets,
self.default_budget,
self.resize,
OptionTerm(self.expect_artifacts)
)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct EmbedderOptions {
#[cfg_attr(feature = "serde", serde(default))]
text: TextOptions,
#[cfg_attr(feature = "serde", serde(default))]
audio: Option<AudioOptions>,
#[cfg_attr(feature = "serde", serde(default))]
vision: Option<ImageOptions>,
}
impl Default for EmbedderOptions {
fn default() -> Self {
Self::new()
}
}
impl EmbedderOptions {
pub const fn new() -> Self {
Self {
text: TextOptions::new(),
audio: None,
vision: None,
}
}
#[inline]
pub const fn text(&self) -> &TextOptions {
&self.text
}
#[must_use]
pub fn with_text(mut self, text: TextOptions) -> Self {
self.set_text(text);
self
}
pub fn set_text(&mut self, text: TextOptions) -> &mut Self {
self.text = text;
self
}
#[inline]
pub const fn audio(&self) -> Option<&AudioOptions> {
self.audio.as_ref()
}
#[must_use]
pub fn with_audio(mut self, audio: AudioOptions) -> Self {
self.set_audio(Some(audio));
self
}
pub fn set_audio(&mut self, audio: Option<AudioOptions>) -> &mut Self {
self.audio = audio;
self
}
#[inline]
pub const fn vision(&self) -> Option<&ImageOptions> {
self.vision.as_ref()
}
#[inline]
pub const fn audio_batch_independent(&self) -> bool {
self.audio_batch_independent_in(&PACKED_SLOT_INVARIANT)
}
#[inline]
pub(crate) const fn audio_batch_independent_in(&self, table: &PlacementTable<bool>) -> bool {
let batching = match &self.audio {
Some(audio) => audio.batching,
None => DEFAULT_AUDIO_BATCHING,
};
batching.batch_independent_in(self.text.coreml.compute(), table)
}
#[inline]
pub const fn image_batch_independent(&self) -> bool {
self.image_batch_independent_in(&PACKED_SLOT_INVARIANT)
}
#[inline]
pub(crate) const fn image_batch_independent_in(&self, table: &PlacementTable<bool>) -> bool {
let batching = match &self.vision {
Some(vision) => vision.batching,
None => DEFAULT_VISION_BATCHING,
};
batching.batch_independent_in(self.text.coreml.compute(), table)
}
#[must_use]
pub fn with_vision(mut self, vision: ImageOptions) -> Self {
self.set_vision(Some(vision));
self
}
pub fn set_vision(&mut self, vision: Option<ImageOptions>) -> &mut Self {
self.vision = vision;
self
}
pub fn validate(&self) -> Result<()> {
self.text.validate()?;
if let Some(vision) = &self.vision {
vision.validate()?;
}
Ok(())
}
}
impl core::fmt::Display for EmbedderOptions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "text=({}),audio=", self.text)?;
match &self.audio {
Some(audio) => write!(f, "({audio})")?,
None => f.write_str("none")?,
}
f.write_str(",vision=")?;
match &self.vision {
Some(vision) => write!(f, "({vision})"),
None => f.write_str("none"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct DerivationTerms<'a>(Terms<'a>);
#[derive(Debug, Clone, Copy)]
enum Terms<'a> {
Text(PartTerms<'a, TextOptions>),
Audio(PartTerms<'a, AudioOptions>),
Vision(PartTerms<'a, ImageOptions>),
}
#[derive(Debug)]
struct PartTerms<'a, O> {
artifacts: ArtifactLabel,
options: &'a O,
}
impl<O> Clone for PartTerms<'_, O> {
fn clone(&self) -> Self {
*self
}
}
impl<O> Copy for PartTerms<'_, O> {}
impl<'a> DerivationTerms<'a> {
pub(crate) const fn text(artifacts: ArtifactLabel, options: &'a TextOptions) -> Self {
Self(Terms::Text(PartTerms { artifacts, options }))
}
pub(crate) const fn audio(artifacts: ArtifactLabel, options: &'a AudioOptions) -> Self {
Self(Terms::Audio(PartTerms { artifacts, options }))
}
pub(crate) const fn vision(artifacts: ArtifactLabel, options: &'a ImageOptions) -> Self {
Self(Terms::Vision(PartTerms { artifacts, options }))
}
}
impl core::fmt::Display for DerivationTerms<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.0 {
Terms::Text(PartTerms { artifacts, options }) => {
let geometry = options.long_text.window_options();
write!(
f,
"embeddinggemma2/text(artifacts={artifacts},coreml=({}),batching={},split_special_tokens={},window=(window={},hop={},tail={}),fold={})",
options.coreml,
options.batching,
options.split_special_tokens,
geometry.window(),
geometry.hop(),
geometry.tail(),
options.fold
)
}
Terms::Audio(PartTerms { artifacts, options }) => write!(
f,
"embeddinggemma2/audio(artifacts={artifacts},precision={},coreml=({}),batching={},hop_samples={},min_tail_samples={})",
options.precision,
options.coreml,
options.batching,
options.hop_samples,
options.min_tail_samples
),
Terms::Vision(PartTerms { artifacts, options }) => write!(
f,
"embeddinggemma2/vision(artifacts={artifacts},coreml=({}),batching={},default_budget={},resize={})",
options.coreml, options.batching, options.default_budget, options.resize
),
}
}
}
#[cfg(test)]
mod tests;