Files
Genarrative/server-rs/crates/platform-audio/src/request.rs
T
kdletters 29e84a77d1
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 5m31s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 5m31s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m47s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m48s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m43s
Project CI / AI game creator shell Rust crates (push) Successful in 2m3s
Project CI / Repository checks (push) Successful in 3m43s
Project CI / Frontend tests (push) Successful in 4m53s
Project CI / Native shell tests (push) Successful in 6m35s
Project CI / Backend tests (push) Successful in 7m25s
Project CI / AI game creator shell web tests (push) Successful in 2m35s
切换 Tiantoken 并删除旧 Vidu 音效
新增 TIANTOKEN_BASE_URL / TIANTOKEN_API_KEY 配置并切换文本与图片链路

保留 Suno VectorEngine 路径,删除旧 Vidu submit/poll/builder 实现

同步脚本、文档、测试与运行时 provider 路由
2026-09-15 19:43:35 +08:00

91 lines
2.9 KiB
Rust

use serde_json::{Map, Value, json};
use crate::{
AudioError, BackgroundMusicTaskRequest, EditorBackgroundMusicTaskRequest, SUNO_DEFAULT_MODEL,
SUNO_PROMPT_MAX_CHARS, SUNO_TAGS_MAX_CHARS, SUNO_TITLE_MAX_CHARS,
};
pub fn build_background_music_task_body(
request: BackgroundMusicTaskRequest,
) -> Result<Value, AudioError> {
let prompt =
normalize_limited_text_allow_empty(&request.prompt, "prompt", SUNO_PROMPT_MAX_CHARS)?;
let title = normalize_limited_text(&request.title, "title", SUNO_TITLE_MAX_CHARS)?;
let tags = request
.tags
.as_deref()
.map(|value| normalize_limited_text(value, "tags", SUNO_TAGS_MAX_CHARS))
.transpose()?;
let model = normalize_optional_text(request.model.as_deref())
.unwrap_or_else(|| SUNO_DEFAULT_MODEL.to_string());
let mut body = Map::from_iter([
("prompt".to_string(), Value::String(prompt)),
("mv".to_string(), Value::String(model)),
("title".to_string(), Value::String(title)),
("task".to_string(), Value::String("generate".to_string())),
(
"make_instrumental".to_string(),
Value::Bool(request.instrumental),
),
]);
if let Some(tags) = tags {
body.insert("tags".to_string(), Value::String(tags));
}
Ok(Value::Object(body))
}
pub fn build_editor_background_music_task_body(
request: EditorBackgroundMusicTaskRequest,
) -> Result<Value, AudioError> {
let prompt =
crate::validate_background_music_generation_prompt(&request.gpt_description_prompt)?;
let model = normalize_optional_text(request.model.as_deref())
.unwrap_or_else(|| SUNO_DEFAULT_MODEL.to_string());
Ok(json!({
"mv": model,
"gpt_description_prompt": prompt.prompt,
"make_instrumental": request.make_instrumental,
}))
}
pub fn normalize_limited_text(
value: &str,
field: &'static str,
max_chars: usize,
) -> Result<String, AudioError> {
let normalized = value.trim().to_string();
if normalized.is_empty() {
return Err(AudioError::invalid_request(format!("{field} 不能为空")));
}
if normalized.chars().count() > max_chars {
return Err(AudioError::invalid_request(format!(
"{field} 超过 {} 字符",
max_chars
)));
}
Ok(normalized)
}
pub fn normalize_limited_text_allow_empty(
value: &str,
field: &'static str,
max_chars: usize,
) -> Result<String, AudioError> {
let normalized = value.trim().to_string();
if normalized.chars().count() > max_chars {
return Err(AudioError::invalid_request(format!(
"{field} 超过 {} 字符",
max_chars
)));
}
Ok(normalized)
}
pub fn normalize_optional_text(value: Option<&str>) -> Option<String> {
value
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
}