From 5926bf38a62f94d79fd94a785cf2b930beea254d Mon Sep 17 00:00:00 2001 From: hongqitai <188678175+hongqitai@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:44:29 -0700 Subject: [PATCH] feat(tui): add Hugging Face env aliases Harvested from PR #2780 by @hongqitai. --- config.example.toml | 4 ++- crates/tui/src/config.rs | 62 +++++++++++++++++++++++++++++++--------- docs/CONFIGURATION.md | 3 ++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/config.example.toml b/config.example.toml index 35df687f..e8ff484c 100644 --- a/config.example.toml +++ b/config.example.toml @@ -248,7 +248,7 @@ max_subagents = 10 # optional (1-20) # SGLang: SGLANG_BASE_URL, SGLANG_MODEL, optional SGLANG_API_KEY # vLLM: VLLM_BASE_URL, VLLM_MODEL, optional VLLM_API_KEY # Ollama: OLLAMA_BASE_URL, OLLAMA_MODEL, optional OLLAMA_API_KEY -# Hugging Face: HUGGINGFACE_API_KEY (or HF_TOKEN), HUGGINGFACE_BASE_URL, HUGGINGFACE_MODEL +# Hugging Face: HUGGINGFACE_API_KEY (or HF_TOKEN), HUGGINGFACE_BASE_URL (or HF_BASE_URL), HUGGINGFACE_MODEL (or HF_MODEL) # # Custom DeepSeek-compatible APIs usually do not need a new provider table: # set `provider = "deepseek"` and override [providers.deepseek].base_url/model. @@ -385,6 +385,8 @@ max_subagents = 10 # optional (1-20) # model = "deepseek-coder:1.3b" # or any local Ollama tag # Hugging Face Inference Providers (https://huggingface.co/docs/api-inference) +# Env var aliases: HUGGINGFACE_API_KEY / HF_TOKEN, HUGGINGFACE_BASE_URL / HF_BASE_URL, +# HUGGINGFACE_MODEL / HF_MODEL [providers.huggingface] # api_key = "YOUR_HF_TOKEN" # base_url = "https://router.huggingface.co/v1" diff --git a/crates/tui/src/config.rs b/crates/tui/src/config.rs index 0c858f95..a6647637 100644 --- a/crates/tui/src/config.rs +++ b/crates/tui/src/config.rs @@ -3526,7 +3526,8 @@ fn apply_env_overrides(config: &mut Config) { .base_url = Some(value); } if matches!(config.api_provider(), ApiProvider::Huggingface) - && let Ok(value) = std::env::var("HUGGINGFACE_BASE_URL") + && let Ok(value) = + std::env::var("HUGGINGFACE_BASE_URL").or_else(|_| std::env::var("HF_BASE_URL")) && !value.trim().is_empty() { config @@ -3739,7 +3740,7 @@ fn apply_env_overrides(config: &mut Config) { .model = Some(value); } if matches!(config.api_provider(), ApiProvider::Huggingface) - && let Ok(value) = std::env::var("HUGGINGFACE_MODEL") + && let Ok(value) = std::env::var("HUGGINGFACE_MODEL").or_else(|_| std::env::var("HF_MODEL")) && !value.trim().is_empty() { config @@ -5931,7 +5932,9 @@ mod tests { huggingface_api_key: Option, huggingface_token: Option, huggingface_base_url: Option, + hf_base_url: Option, huggingface_model: Option, + hf_model: Option, } impl EnvGuard { @@ -6028,7 +6031,9 @@ mod tests { let huggingface_api_key_prev = env::var_os("HUGGINGFACE_API_KEY"); let huggingface_token_prev = env::var_os("HF_TOKEN"); let huggingface_base_url_prev = env::var_os("HUGGINGFACE_BASE_URL"); + let hf_base_url_prev = env::var_os("HF_BASE_URL"); let huggingface_model_prev = env::var_os("HUGGINGFACE_MODEL"); + let hf_model_prev = env::var_os("HF_MODEL"); // Safety: test-only environment mutation guarded by a global mutex. unsafe { env::set_var("HOME", &home_str); @@ -6120,7 +6125,9 @@ mod tests { env::remove_var("HUGGINGFACE_API_KEY"); env::remove_var("HF_TOKEN"); env::remove_var("HUGGINGFACE_BASE_URL"); + env::remove_var("HF_BASE_URL"); env::remove_var("HUGGINGFACE_MODEL"); + env::remove_var("HF_MODEL"); } Self { home: home_prev, @@ -6212,7 +6219,9 @@ mod tests { huggingface_api_key: huggingface_api_key_prev, huggingface_token: huggingface_token_prev, huggingface_base_url: huggingface_base_url_prev, + hf_base_url: hf_base_url_prev, huggingface_model: huggingface_model_prev, + hf_model: hf_model_prev, } } } @@ -6322,7 +6331,9 @@ mod tests { Self::restore_var("HUGGINGFACE_API_KEY", self.huggingface_api_key.take()); Self::restore_var("HF_TOKEN", self.huggingface_token.take()); Self::restore_var("HUGGINGFACE_BASE_URL", self.huggingface_base_url.take()); + Self::restore_var("HF_BASE_URL", self.hf_base_url.take()); Self::restore_var("HUGGINGFACE_MODEL", self.huggingface_model.take()); + Self::restore_var("HF_MODEL", self.hf_model.take()); } } } @@ -10349,21 +10360,44 @@ model = "deepseek-ai/deepseek-v4-pro" std::process::id(), nanos )); - fs::create_dir_all(&temp_root)?; - let _guard = EnvGuard::new(&temp_root); - unsafe { - env::set_var("CODEWHALE_PROVIDER", "huggingface"); - env::set_var("HUGGINGFACE_API_KEY", "hf-env-key"); - env::set_var("HUGGINGFACE_BASE_URL", "https://custom-hf.example/v1"); - env::set_var("HUGGINGFACE_MODEL", "meta-llama/Llama-3-70B"); + { + let long_form_root = temp_root.join("long-form"); + fs::create_dir_all(&long_form_root)?; + let _guard = EnvGuard::new(&long_form_root); + + unsafe { + env::set_var("CODEWHALE_PROVIDER", "huggingface"); + env::set_var("HUGGINGFACE_API_KEY", "hf-env-key"); + env::set_var("HUGGINGFACE_BASE_URL", "https://custom-hf.example/v1"); + env::set_var("HUGGINGFACE_MODEL", "meta-llama/Llama-3-70B"); + } + + let config = Config::load(None, None)?; + assert_eq!(config.api_provider(), ApiProvider::Huggingface); + assert_eq!(config.deepseek_api_key()?, "hf-env-key"); + assert_eq!(config.deepseek_base_url(), "https://custom-hf.example/v1"); + assert_eq!(config.default_model(), "meta-llama/Llama-3-70B"); } - let config = Config::load(None, None)?; - assert_eq!(config.api_provider(), ApiProvider::Huggingface); - assert_eq!(config.deepseek_api_key()?, "hf-env-key"); - assert_eq!(config.deepseek_base_url(), "https://custom-hf.example/v1"); - assert_eq!(config.default_model(), "meta-llama/Llama-3-70B"); + { + let short_form_root = temp_root.join("short-form"); + fs::create_dir_all(&short_form_root)?; + let _guard = EnvGuard::new(&short_form_root); + + unsafe { + env::set_var("CODEWHALE_PROVIDER", "huggingface"); + env::set_var("HF_TOKEN", "hf-env-key"); + env::set_var("HF_BASE_URL", "https://custom-hf.example/v1"); + env::set_var("HF_MODEL", "meta-llama/Llama-3-70B"); + } + + let config = Config::load(None, None)?; + assert_eq!(config.api_provider(), ApiProvider::Huggingface); + assert_eq!(config.deepseek_api_key()?, "hf-env-key"); + assert_eq!(config.deepseek_base_url(), "https://custom-hf.example/v1"); + assert_eq!(config.default_model(), "meta-llama/Llama-3-70B"); + } Ok(()) } diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 25206184..cd5082a4 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -441,6 +441,9 @@ Remaining variables: - `NOVITA_BASE_URL` - `FIREWORKS_API_KEY` - `FIREWORKS_BASE_URL` +- `HUGGINGFACE_API_KEY` or `HF_TOKEN` (`HF_TOKEN` is a fallback alias accepted when provider is `huggingface`) +- `HUGGINGFACE_BASE_URL` or `HF_BASE_URL` +- `HUGGINGFACE_MODEL` or `HF_MODEL` - `SILICONFLOW_API_KEY` - `SILICONFLOW_BASE_URL` - `SILICONFLOW_MODEL`