use std::collections::BTreeMap;
pub mod request {
pub const LANGUAGE: &str = "language";
pub const RETURN_TIMESTAMPS: &str = "return_timestamps";
pub const CLAMP_TIMESTAMPS_TO_AUDIO: &str = "clamp_timestamps_to_audio";
pub const AUDIO_CHUNK_SECONDS: &str = "audio_chunk_seconds";
pub const AUDIO_CHUNK_MODE: &str = "audio_chunk_mode";
pub const THRESHOLD: &str = "threshold";
pub const REFERENCE_TEXT: &str = "reference_text";
pub const RETRY_BADCASE: &str = "retry_badcase";
pub const STYLE: &str = "style";
pub const LYRICS: &str = "lyrics";
pub const SEED: &str = "seed";
pub const MAX_TOKENS: &str = "max_tokens";
pub const TEXT_NORMALIZATION: &str = "text_normalization";
}
pub mod session {
pub const SESSION_LEN_SEC: &str = "session_len_sec";
pub const GRAPH_CAPACITY_MODE: &str = "graph_capacity_mode";
pub const QWEN3_ASR_FORCED_ALIGNER_MODEL_PATH: &str = "qwen3_asr.forced_aligner_model_path";
pub mod graph_capacity {
pub const FIXED: &str = "fixed";
pub const TIERED: &str = "tiered";
pub const GROW: &str = "grow";
pub const DOUBLE: &str = "double";
}
pub mod audio_chunk_mode {
pub const AUTO: &str = "auto";
pub const FIXED: &str = "fixed";
pub const QUIET_ENERGY: &str = "quiet_energy";
pub const VAD: &str = "vad";
pub const NONE: &str = "none";
}
}
#[derive(Debug, Clone, Default)]
pub struct SessionOptions {
options: BTreeMap<String, String>,
}
impl SessionOptions {
pub fn new() -> Self {
Self::default()
}
pub fn option(mut self, key: impl Into<String>, value: impl ToString) -> Self {
self.options.insert(key.into(), value.to_string());
self
}
pub fn session_len_sec(mut self, seconds: f64) -> Self {
self.options
.insert(session::SESSION_LEN_SEC.to_owned(), format!("{seconds:?}"));
self
}
pub fn graph_capacity_mode(mut self, mode: impl Into<String>) -> Self {
self.options
.insert(session::GRAPH_CAPACITY_MODE.to_owned(), mode.into());
self
}
pub fn qwen3_aligner_model_path(mut self, path: impl Into<String>) -> Self {
self.options.insert(
session::QWEN3_ASR_FORCED_ALIGNER_MODEL_PATH.to_owned(),
path.into(),
);
self
}
pub fn to_json(&self) -> String {
if self.options.is_empty() {
return "{}".to_string();
}
let mut inner = serde_json::Map::new();
for (k, v) in &self.options {
let value = if k == session::SESSION_LEN_SEC {
v.parse::<f64>().map_or_else(
|_| serde_json::Value::String(v.clone()),
serde_json::Value::from,
)
} else {
serde_json::Value::String(v.clone())
};
inner.insert(k.clone(), value);
}
let mut obj = serde_json::Map::new();
obj.insert("options".to_string(), serde_json::Value::Object(inner));
serde_json::Value::Object(obj).to_string()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn session_options_json_shape() {
let opts = SessionOptions::new()
.session_len_sec(100.0)
.graph_capacity_mode(session::graph_capacity::GROW);
let v: serde_json::Value = serde_json::from_str(&opts.to_json()).unwrap();
assert_eq!(
v,
serde_json::json!({
"options": {
"session_len_sec": 100.0,
"graph_capacity_mode": "grow",
}
})
);
}
#[test]
fn session_options_empty_and_custom() {
assert_eq!(SessionOptions::new().to_json(), "{}");
let opts = SessionOptions::new()
.qwen3_aligner_model_path("./align.gguf")
.option("custom_key", "v");
let v: serde_json::Value = serde_json::from_str(&opts.to_json()).unwrap();
assert_eq!(
v,
serde_json::json!({
"options": {
"qwen3_asr.forced_aligner_model_path": "./align.gguf",
"custom_key": "v",
}
})
);
}
}