diff --git a/docs/technical/API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md b/docs/technical/API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md index a89274c3..10927240 100644 --- a/docs/technical/API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md +++ b/docs/technical/API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md @@ -74,6 +74,7 @@ ARK_CHARACTER_VIDEO_MODEL / DASHSCOPE_CHARACTER_VIDEO_MODEL 3. 文本 LLM provider 为 `ark` 且未配置 `GENARRATIVE_LLM_BASE_URL` 时,仍回退到 Ark 公开基础 URL。 4. 角色视频 provider 复用 Ark 且未配置 `ARK_CHARACTER_VIDEO_BASE_URL` 时,仍回退到 Ark 公开基础 URL。 5. 具体模型名缺失时不在配置层伪造默认模型,调用到对应能力时由下游配置校验返回缺配置错误。 +6. 本地开发启动脚本与 Rust 入口会按 `.env` → `.env.local` → `.env.secrets.local` 逐级覆盖本地变量;`.env.secrets.local` 作为最后一级,会覆盖前两者同名变量。 ## 示例文件 diff --git a/scripts/api-server-dev.mjs b/scripts/api-server-dev.mjs index c90de96f..20e9c6e4 100644 --- a/scripts/api-server-dev.mjs +++ b/scripts/api-server-dev.mjs @@ -8,7 +8,7 @@ const apiServerExePath = resolve( 'server-rs/target/debug/api-server.exe', ); -function loadEnvFile(path, target) { +function loadEnvFile(path, target, override = false) { if (!existsSync(path)) { return; } @@ -26,7 +26,7 @@ function loadEnvFile(path, target) { } const [, key, rawValue] = match; - if (target[key] !== undefined) { + if (!override && target[key] !== undefined) { continue; } @@ -36,8 +36,8 @@ function loadEnvFile(path, target) { const mergedEnv = { ...process.env }; loadEnvFile(resolve(repoRoot, '.env'), mergedEnv); -loadEnvFile(resolve(repoRoot, '.env.local'), mergedEnv); -loadEnvFile(resolve(repoRoot, '.env.secrets.local'), mergedEnv); +loadEnvFile(resolve(repoRoot, '.env.local'), mergedEnv, true); +loadEnvFile(resolve(repoRoot, '.env.secrets.local'), mergedEnv, true); mergedEnv.GENARRATIVE_API_HOST = mergedEnv.GENARRATIVE_API_HOST || '127.0.0.1'; mergedEnv.GENARRATIVE_API_PORT = mergedEnv.GENARRATIVE_API_PORT || '3100'; diff --git a/server-rs/crates/api-server/src/llm.rs b/server-rs/crates/api-server/src/llm.rs index c4944c6b..7d359e3e 100644 --- a/server-rs/crates/api-server/src/llm.rs +++ b/server-rs/crates/api-server/src/llm.rs @@ -192,6 +192,7 @@ mod tests { let state = seed_authenticated_state(AppConfig { llm_base_url: server_url, llm_api_key: Some("test-key".to_string()), + llm_model: "ark-router-test".to_string(), ..AppConfig::default() }) .await; @@ -267,6 +268,7 @@ mod tests { let state = seed_authenticated_state(AppConfig { llm_base_url: server_url, llm_api_key: Some("test-key".to_string()), + llm_model: "ark-router-test".to_string(), ..AppConfig::default() }) .await; diff --git a/server-rs/crates/api-server/src/main.rs b/server-rs/crates/api-server/src/main.rs index 96d76558..ff5e8a8c 100644 --- a/server-rs/crates/api-server/src/main.rs +++ b/server-rs/crates/api-server/src/main.rs @@ -99,10 +99,10 @@ fn run_api_server_with_runtime() -> Result<(), std::io::Error> { } async fn run_api_server() -> Result<(), std::io::Error> { - // 运行本地开发与联调时,优先从仓库根目录加载本地变量,避免手工逐项导出 OSS / APIMart 配置。 + // 运行本地开发与联调时,按 .env -> .env.local -> .env.secrets.local 逐级覆盖本地变量。 let _ = dotenvy::from_filename(".env"); - let _ = dotenvy::from_filename(".env.local"); - let _ = dotenvy::from_filename(".env.secrets.local"); + let _ = dotenvy::from_filename_override(".env.local"); + let _ = dotenvy::from_filename_override(".env.secrets.local"); // 统一先从配置对象读取监听地址,避免后续把环境变量读取散落到入口和路由层。 let config = AppConfig::from_env();