Merge origin/master into codex/wechat
This commit is contained in:
@@ -4,8 +4,12 @@ use platform_llm::{
|
||||
DEFAULT_ARK_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS,
|
||||
DEFAULT_RETRY_BACKOFF_MS, LlmProvider,
|
||||
};
|
||||
use platform_speech::{
|
||||
DEFAULT_ASR_RESOURCE_ID, DEFAULT_ASR_WS_URL,
|
||||
DEFAULT_REQUEST_TIMEOUT_MS as DEFAULT_SPEECH_REQUEST_TIMEOUT_MS,
|
||||
DEFAULT_TTS_BIDIRECTION_WS_URL, DEFAULT_TTS_RESOURCE_ID, DEFAULT_TTS_SSE_URL,
|
||||
};
|
||||
|
||||
const DEFAULT_LLM_MODEL: &str = "doubao-1-5-pro-32k-character-250715";
|
||||
const DEFAULT_INTERNAL_API_SECRET: &str = "genarrative-dev-internal-bridge";
|
||||
const DEFAULT_AUTH_STORE_PATH: &str = "server-rs/.data/auth-store.json";
|
||||
const SPACETIME_LOCAL_CONFIG_FILE: &str = "spacetime.local.json";
|
||||
@@ -95,7 +99,22 @@ pub struct AppConfig {
|
||||
pub dashscope_image_request_timeout_ms: u64,
|
||||
pub apimart_base_url: String,
|
||||
pub apimart_api_key: Option<String>,
|
||||
pub apimart_image_request_timeout_ms: u64,
|
||||
pub vector_engine_base_url: String,
|
||||
pub vector_engine_api_key: Option<String>,
|
||||
pub vector_engine_image_request_timeout_ms: u64,
|
||||
pub vector_engine_audio_request_timeout_ms: u64,
|
||||
pub hyper3d_base_url: String,
|
||||
pub hyper3d_api_key: Option<String>,
|
||||
pub hyper3d_model_request_timeout_ms: u64,
|
||||
pub volcengine_speech_api_key: Option<String>,
|
||||
pub volcengine_speech_app_id: Option<String>,
|
||||
pub volcengine_speech_access_key: Option<String>,
|
||||
pub volcengine_speech_asr_resource_id: String,
|
||||
pub volcengine_speech_tts_resource_id: String,
|
||||
pub volcengine_speech_asr_ws_url: String,
|
||||
pub volcengine_speech_tts_bidirection_ws_url: String,
|
||||
pub volcengine_speech_tts_sse_url: String,
|
||||
pub volcengine_speech_request_timeout_ms: u64,
|
||||
pub draft_asset_generation_max_concurrent_requests: usize,
|
||||
pub ark_character_video_base_url: String,
|
||||
pub ark_character_video_api_key: Option<String>,
|
||||
@@ -178,9 +197,9 @@ impl Default for AppConfig {
|
||||
spacetime_pool_size: 4,
|
||||
spacetime_procedure_timeout: Duration::from_secs(30),
|
||||
llm_provider: LlmProvider::Ark,
|
||||
llm_base_url: DEFAULT_ARK_BASE_URL.to_string(),
|
||||
llm_base_url: String::new(),
|
||||
llm_api_key: None,
|
||||
llm_model: DEFAULT_LLM_MODEL.to_string(),
|
||||
llm_model: String::new(),
|
||||
llm_request_timeout_ms: DEFAULT_REQUEST_TIMEOUT_MS,
|
||||
llm_max_retries: DEFAULT_MAX_RETRIES,
|
||||
llm_retry_backoff_ms: DEFAULT_RETRY_BACKOFF_MS,
|
||||
@@ -188,18 +207,33 @@ impl Default for AppConfig {
|
||||
creation_agent_llm_web_search_enabled: true,
|
||||
dashscope_base_url: "https://dashscope.aliyuncs.com/api/v1".to_string(),
|
||||
dashscope_api_key: None,
|
||||
dashscope_scene_image_model: "wan2.2-t2i-flash".to_string(),
|
||||
dashscope_reference_image_model: "qwen-image-2.0".to_string(),
|
||||
dashscope_cover_image_model: "wan2.2-t2i-flash".to_string(),
|
||||
dashscope_scene_image_model: String::new(),
|
||||
dashscope_reference_image_model: String::new(),
|
||||
dashscope_cover_image_model: String::new(),
|
||||
dashscope_image_request_timeout_ms: 150_000,
|
||||
apimart_base_url: "https://api.apimart.ai/v1".to_string(),
|
||||
apimart_base_url: String::new(),
|
||||
apimart_api_key: None,
|
||||
apimart_image_request_timeout_ms: 180_000,
|
||||
vector_engine_base_url: String::new(),
|
||||
vector_engine_api_key: None,
|
||||
vector_engine_image_request_timeout_ms: 180_000,
|
||||
vector_engine_audio_request_timeout_ms: 180_000,
|
||||
hyper3d_base_url: "https://api.hyper3d.com/api/v2".to_string(),
|
||||
hyper3d_api_key: None,
|
||||
hyper3d_model_request_timeout_ms: 180_000,
|
||||
volcengine_speech_api_key: None,
|
||||
volcengine_speech_app_id: None,
|
||||
volcengine_speech_access_key: None,
|
||||
volcengine_speech_asr_resource_id: DEFAULT_ASR_RESOURCE_ID.to_string(),
|
||||
volcengine_speech_tts_resource_id: DEFAULT_TTS_RESOURCE_ID.to_string(),
|
||||
volcengine_speech_asr_ws_url: DEFAULT_ASR_WS_URL.to_string(),
|
||||
volcengine_speech_tts_bidirection_ws_url: DEFAULT_TTS_BIDIRECTION_WS_URL.to_string(),
|
||||
volcengine_speech_tts_sse_url: DEFAULT_TTS_SSE_URL.to_string(),
|
||||
volcengine_speech_request_timeout_ms: DEFAULT_SPEECH_REQUEST_TIMEOUT_MS,
|
||||
draft_asset_generation_max_concurrent_requests: 4,
|
||||
ark_character_video_base_url: DEFAULT_ARK_BASE_URL.to_string(),
|
||||
ark_character_video_base_url: String::new(),
|
||||
ark_character_video_api_key: None,
|
||||
ark_character_video_request_timeout_ms: 420_000,
|
||||
ark_character_video_model: "doubao-seedance-2-0-fast-260128".to_string(),
|
||||
ark_character_video_model: String::new(),
|
||||
character_animation_ffmpeg_path: "ffmpeg".to_string(),
|
||||
character_animation_ffprobe_path: "ffprobe".to_string(),
|
||||
character_animation_frame_extract_timeout_ms: 120_000,
|
||||
@@ -472,6 +506,8 @@ impl AppConfig {
|
||||
read_first_non_empty_env(&["GENARRATIVE_LLM_BASE_URL", "LLM_BASE_URL"])
|
||||
{
|
||||
config.llm_base_url = llm_base_url;
|
||||
} else if config.llm_provider == LlmProvider::Ark {
|
||||
config.llm_base_url = DEFAULT_ARK_BASE_URL.to_string();
|
||||
}
|
||||
|
||||
config.llm_api_key =
|
||||
@@ -553,10 +589,73 @@ impl AppConfig {
|
||||
|
||||
config.apimart_api_key = read_first_non_empty_env(&["APIMART_API_KEY"]);
|
||||
|
||||
if let Some(apimart_image_request_timeout_ms) =
|
||||
read_first_positive_u64_env(&["APIMART_IMAGE_REQUEST_TIMEOUT_MS"])
|
||||
if let Some(vector_engine_base_url) = read_first_non_empty_env(&["VECTOR_ENGINE_BASE_URL"])
|
||||
{
|
||||
config.apimart_image_request_timeout_ms = apimart_image_request_timeout_ms;
|
||||
config.vector_engine_base_url = vector_engine_base_url;
|
||||
}
|
||||
|
||||
config.vector_engine_api_key = read_first_non_empty_env(&["VECTOR_ENGINE_API_KEY"]);
|
||||
|
||||
if let Some(vector_engine_image_request_timeout_ms) =
|
||||
read_first_positive_u64_env(&["VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS"])
|
||||
{
|
||||
config.vector_engine_image_request_timeout_ms = vector_engine_image_request_timeout_ms;
|
||||
}
|
||||
|
||||
if let Some(vector_engine_audio_request_timeout_ms) =
|
||||
read_first_positive_u64_env(&["VECTOR_ENGINE_AUDIO_REQUEST_TIMEOUT_MS"])
|
||||
{
|
||||
config.vector_engine_audio_request_timeout_ms = vector_engine_audio_request_timeout_ms;
|
||||
}
|
||||
|
||||
if let Some(hyper3d_base_url) =
|
||||
read_first_non_empty_env(&["HYPER3D_BASE_URL", "RODIN_BASE_URL"])
|
||||
{
|
||||
config.hyper3d_base_url = hyper3d_base_url;
|
||||
}
|
||||
|
||||
config.hyper3d_api_key = read_first_non_empty_env(&["HYPER3D_API_KEY", "RODIN_API_KEY"]);
|
||||
|
||||
if let Some(hyper3d_model_request_timeout_ms) = read_first_positive_u64_env(&[
|
||||
"HYPER3D_MODEL_REQUEST_TIMEOUT_MS",
|
||||
"RODIN_MODEL_REQUEST_TIMEOUT_MS",
|
||||
]) {
|
||||
config.hyper3d_model_request_timeout_ms = hyper3d_model_request_timeout_ms;
|
||||
}
|
||||
|
||||
config.volcengine_speech_api_key =
|
||||
read_first_non_empty_env(&["VOLCENGINE_SPEECH_API_KEY", "VOLCENGINE_API_KEY"]);
|
||||
config.volcengine_speech_app_id =
|
||||
read_first_non_empty_env(&["VOLCENGINE_SPEECH_APP_ID", "VOLCENGINE_ACCESS_KEY_ID"]);
|
||||
config.volcengine_speech_access_key = read_first_non_empty_env(&[
|
||||
"VOLCENGINE_SPEECH_ACCESS_KEY",
|
||||
"VOLCENGINE_SECRET_ACCESS_KEY",
|
||||
]);
|
||||
if let Some(asr_resource_id) =
|
||||
read_first_non_empty_env(&["VOLCENGINE_SPEECH_ASR_RESOURCE_ID"])
|
||||
{
|
||||
config.volcengine_speech_asr_resource_id = asr_resource_id;
|
||||
}
|
||||
if let Some(tts_resource_id) =
|
||||
read_first_non_empty_env(&["VOLCENGINE_SPEECH_TTS_RESOURCE_ID"])
|
||||
{
|
||||
config.volcengine_speech_tts_resource_id = tts_resource_id;
|
||||
}
|
||||
if let Some(asr_ws_url) = read_first_non_empty_env(&["VOLCENGINE_SPEECH_ASR_WS_URL"]) {
|
||||
config.volcengine_speech_asr_ws_url = asr_ws_url;
|
||||
}
|
||||
if let Some(tts_bidirection_ws_url) =
|
||||
read_first_non_empty_env(&["VOLCENGINE_SPEECH_TTS_BIDIRECTION_WS_URL"])
|
||||
{
|
||||
config.volcengine_speech_tts_bidirection_ws_url = tts_bidirection_ws_url;
|
||||
}
|
||||
if let Some(tts_sse_url) = read_first_non_empty_env(&["VOLCENGINE_SPEECH_TTS_SSE_URL"]) {
|
||||
config.volcengine_speech_tts_sse_url = tts_sse_url;
|
||||
}
|
||||
if let Some(request_timeout_ms) =
|
||||
read_first_positive_u64_env(&["VOLCENGINE_SPEECH_REQUEST_TIMEOUT_MS"])
|
||||
{
|
||||
config.volcengine_speech_request_timeout_ms = request_timeout_ms;
|
||||
}
|
||||
|
||||
if let Some(max_concurrent_requests) = read_first_usize_env(&[
|
||||
@@ -573,6 +672,8 @@ impl AppConfig {
|
||||
"LLM_BASE_URL",
|
||||
]) {
|
||||
config.ark_character_video_base_url = ark_character_video_base_url;
|
||||
} else if config.llm_provider == LlmProvider::Ark {
|
||||
config.ark_character_video_base_url = DEFAULT_ARK_BASE_URL.to_string();
|
||||
}
|
||||
|
||||
config.ark_character_video_api_key = read_first_non_empty_env(&[
|
||||
@@ -832,11 +933,118 @@ fn parse_positive_u16(raw: &str) -> Option<u16> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::AppConfig;
|
||||
use super::{AppConfig, LlmProvider};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
|
||||
#[test]
|
||||
fn default_keeps_non_public_model_and_base_url_empty() {
|
||||
let config = AppConfig::default();
|
||||
|
||||
assert!(config.llm_model.is_empty());
|
||||
assert!(config.llm_base_url.is_empty());
|
||||
assert!(config.apimart_base_url.is_empty());
|
||||
assert!(config.vector_engine_base_url.is_empty());
|
||||
assert!(config.ark_character_video_base_url.is_empty());
|
||||
assert_eq!(config.hyper3d_base_url, "https://api.hyper3d.com/api/v2");
|
||||
assert!(config.ark_character_video_model.is_empty());
|
||||
assert!(config.dashscope_scene_image_model.is_empty());
|
||||
assert!(config.dashscope_reference_image_model.is_empty());
|
||||
assert!(config.dashscope_cover_image_model.is_empty());
|
||||
assert_eq!(
|
||||
config.dashscope_base_url,
|
||||
"https://dashscope.aliyuncs.com/api/v1"
|
||||
);
|
||||
assert_eq!(config.sms_endpoint, "dypnsapi.aliyuncs.com");
|
||||
assert_eq!(
|
||||
config.wechat_authorize_endpoint,
|
||||
"https://open.weixin.qq.com/connect/qrconnect"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_env_reads_non_public_models_and_urls() {
|
||||
let _guard = ENV_LOCK
|
||||
.get_or_init(|| Mutex::new(()))
|
||||
.lock()
|
||||
.expect("env lock should not poison");
|
||||
|
||||
unsafe {
|
||||
std::env::remove_var("GENARRATIVE_LLM_PROVIDER");
|
||||
std::env::remove_var("GENARRATIVE_LLM_BASE_URL");
|
||||
std::env::remove_var("GENARRATIVE_LLM_MODEL");
|
||||
std::env::remove_var("APIMART_BASE_URL");
|
||||
std::env::remove_var("VECTOR_ENGINE_BASE_URL");
|
||||
std::env::remove_var("VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS");
|
||||
std::env::remove_var("HYPER3D_BASE_URL");
|
||||
std::env::remove_var("DASHSCOPE_SCENE_IMAGE_MODEL");
|
||||
std::env::remove_var("DASHSCOPE_REFERENCE_IMAGE_MODEL");
|
||||
std::env::remove_var("DASHSCOPE_COVER_IMAGE_MODEL");
|
||||
std::env::remove_var("ARK_CHARACTER_VIDEO_BASE_URL");
|
||||
std::env::remove_var("ARK_CHARACTER_VIDEO_MODEL");
|
||||
std::env::set_var("GENARRATIVE_LLM_PROVIDER", "openai-compatible");
|
||||
std::env::set_var(
|
||||
"GENARRATIVE_LLM_BASE_URL",
|
||||
"https://llm.internal.example/v1",
|
||||
);
|
||||
std::env::set_var("GENARRATIVE_LLM_MODEL", "internal-text-model");
|
||||
std::env::set_var("APIMART_BASE_URL", "https://responses.internal.example/v1");
|
||||
std::env::set_var("VECTOR_ENGINE_BASE_URL", "https://vector.internal.example");
|
||||
std::env::set_var("VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS", "210000");
|
||||
std::env::set_var("HYPER3D_BASE_URL", "https://model.internal.example/api/v2");
|
||||
std::env::set_var("DASHSCOPE_SCENE_IMAGE_MODEL", "scene-model");
|
||||
std::env::set_var("DASHSCOPE_REFERENCE_IMAGE_MODEL", "reference-model");
|
||||
std::env::set_var("DASHSCOPE_COVER_IMAGE_MODEL", "cover-model");
|
||||
std::env::set_var(
|
||||
"ARK_CHARACTER_VIDEO_BASE_URL",
|
||||
"https://video.internal.example/v1",
|
||||
);
|
||||
std::env::set_var("ARK_CHARACTER_VIDEO_MODEL", "video-model");
|
||||
}
|
||||
|
||||
let config = AppConfig::from_env();
|
||||
assert_eq!(config.llm_provider, LlmProvider::OpenAiCompatible);
|
||||
assert_eq!(config.llm_base_url, "https://llm.internal.example/v1");
|
||||
assert_eq!(config.llm_model, "internal-text-model");
|
||||
assert_eq!(
|
||||
config.apimart_base_url,
|
||||
"https://responses.internal.example/v1"
|
||||
);
|
||||
assert_eq!(
|
||||
config.vector_engine_base_url,
|
||||
"https://vector.internal.example"
|
||||
);
|
||||
assert_eq!(config.vector_engine_image_request_timeout_ms, 210_000);
|
||||
assert_eq!(
|
||||
config.hyper3d_base_url,
|
||||
"https://model.internal.example/api/v2"
|
||||
);
|
||||
assert_eq!(config.dashscope_scene_image_model, "scene-model");
|
||||
assert_eq!(config.dashscope_reference_image_model, "reference-model");
|
||||
assert_eq!(config.dashscope_cover_image_model, "cover-model");
|
||||
assert_eq!(
|
||||
config.ark_character_video_base_url,
|
||||
"https://video.internal.example/v1"
|
||||
);
|
||||
assert_eq!(config.ark_character_video_model, "video-model");
|
||||
|
||||
unsafe {
|
||||
std::env::remove_var("GENARRATIVE_LLM_PROVIDER");
|
||||
std::env::remove_var("GENARRATIVE_LLM_BASE_URL");
|
||||
std::env::remove_var("GENARRATIVE_LLM_MODEL");
|
||||
std::env::remove_var("APIMART_BASE_URL");
|
||||
std::env::remove_var("VECTOR_ENGINE_BASE_URL");
|
||||
std::env::remove_var("VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS");
|
||||
std::env::remove_var("HYPER3D_BASE_URL");
|
||||
std::env::remove_var("DASHSCOPE_SCENE_IMAGE_MODEL");
|
||||
std::env::remove_var("DASHSCOPE_REFERENCE_IMAGE_MODEL");
|
||||
std::env::remove_var("DASHSCOPE_COVER_IMAGE_MODEL");
|
||||
std::env::remove_var("ARK_CHARACTER_VIDEO_BASE_URL");
|
||||
std::env::remove_var("ARK_CHARACTER_VIDEO_MODEL");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_env_reads_spacetime_pool_size() {
|
||||
let _guard = ENV_LOCK
|
||||
|
||||
Reference in New Issue
Block a user