Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
89d7399
feat(providers): add Alibaba Coding Plan as provider
dvrd Mar 28, 2026
2c9e368
feat(volcengine): add Volcano Engine (Doubao/Ark) provider support (#49)
dvrd Mar 28, 2026
1fd94c0
feat(volcengine): 添加火山引擎(Doubao)和火山引擎编码计划支持
Mar 20, 2026
c52fa2a
feat(volcengine): 添加对火山引擎和豆包API的支持并更新模型目录
Mar 20, 2026
d74e022
feat(tui): 添加并调整火山引擎提供商配置
Mar 20, 2026
8da98f1
refactor(volcengine): standardize display name for Volcano Engine pro…
Mar 23, 2026
a775561
fix(volcengine): resolve duplicate model IDs, clarify separators, add…
dvrd Mar 28, 2026
2a5a91c
fix(volcengine): display names, ordering, and documentation comments
dvrd Mar 28, 2026
4dea482
fix(volcengine): correct provider probe order, display names, and ali…
dvrd Mar 28, 2026
10a8cc0
fix(volcengine): remove volcengine_coding from auto-detect probe list…
dvrd Mar 28, 2026
c847a3a
fix(volcengine): strip ark/ prefix from Ark marketplace model IDs and…
dvrd Mar 28, 2026
c2a1f1d
fix(volcengine): strip ark/ prefix before sending model id to Ark API
dvrd Mar 28, 2026
39f6596
fix(volcengine): add doubao provider alias to ark/ strip guard and ex…
dvrd Mar 28, 2026
67d1d88
fix(model_catalog): resolve Ark marketplace model ID collisions
dvrd Mar 28, 2026
b3a4c33
fix(volcengine): harden auth detection, disambiguate display names, a…
dvrd Mar 28, 2026
571443e
fix(volcengine): update known_providers count after rebase onto main
dvrd Mar 28, 2026
b958c87
fix(volcengine): resolve post-rebase review findings
dvrd Mar 28, 2026
090b723
fix(volcengine): add DOUBAO_PROVIDER_ID constant and complete provide…
dvrd Mar 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/openfang-channels/src/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl LineAdapter {
diff |= a ^ b;
}
if diff != 0 {
let computed = base64::engine::general_purpose::STANDARD.encode(&result);
let computed = base64::engine::general_purpose::STANDARD.encode(result);
// Log first/last 4 chars of each signature for debugging without leaking full HMAC
let comp_redacted = format!(
"{}...{}",
Expand Down
3 changes: 2 additions & 1 deletion crates/openfang-cli/src/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const PROVIDER_ENV_VARS: &[(&str, &str)] = &[
("ANTHROPIC_API_KEY", "Anthropic"),
("OPENAI_API_KEY", "OpenAI"),
("DEEPSEEK_API_KEY", "DeepSeek"),
("VOLCENGINE_API_KEY", "Volcano Engine"),
("GEMINI_API_KEY", "Gemini"),
("GOOGLE_API_KEY", "Gemini"),
("GROQ_API_KEY", "Groq"),
Expand All @@ -31,7 +32,7 @@ const PROVIDER_ENV_VARS: &[(&str, &str)] = &[

fn detect_provider() -> Option<(&'static str, &'static str)> {
for &(var, name) in PROVIDER_ENV_VARS {
if std::env::var(var).is_ok() {
if std::env::var(var).ok().filter(|v| !v.is_empty()).is_some() {
return Some((name, var));
}
}
Expand Down
20 changes: 20 additions & 0 deletions crates/openfang-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,18 @@ fn provider_list() -> Vec<(&'static str, &'static str, &'static str, &'static st
("groq", "GROQ_API_KEY", "llama-3.3-70b-versatile", "Groq"),
("gemini", "GEMINI_API_KEY", "gemini-2.5-flash", "Gemini"),
("deepseek", "DEEPSEEK_API_KEY", "deepseek-chat", "DeepSeek"),
(
"volcengine",
"VOLCENGINE_API_KEY",
"doubao-seed-1-6-251015",
"Volcano Engine",
),
(
"volcengine_coding",
"VOLCENGINE_API_KEY",
"ark-code-latest",
"Volcano Engine Coding Plan",
),
(
"anthropic",
"ANTHROPIC_API_KEY",
Expand Down Expand Up @@ -4541,6 +4553,7 @@ fn provider_to_env_var(provider: &str) -> String {
"perplexity" => "PERPLEXITY_API_KEY".to_string(),
"cohere" => "COHERE_API_KEY".to_string(),
"xai" => "XAI_API_KEY".to_string(),
"volcengine" | "doubao" | "volcengine_coding" => "VOLCENGINE_API_KEY".to_string(),
"brave" => "BRAVE_API_KEY".to_string(),
"tavily" => "TAVILY_API_KEY".to_string(),
other => format!("{}_API_KEY", other.to_uppercase()),
Expand Down Expand Up @@ -4592,6 +4605,13 @@ pub(crate) fn test_api_key(provider: &str, env_var: &str) -> bool {
.get("https://openrouter.ai/api/v1/models")
.bearer_auth(&key)
.send(),
"volcengine" | "doubao" => {
let base = openfang_types::model_catalog::VOLCENGINE_BASE_URL.trim_end_matches('/');
client.get(format!("{base}/models")).bearer_auth(&key).send()
}
// The Ark Coding endpoint (/api/coding/v3) does not expose a standard
// OpenAI-compatible /models list — skip probing and assume the key is valid.
"volcengine_coding" => return true,
_ => return true, // unknown provider — skip test
};

Expand Down
22 changes: 19 additions & 3 deletions crates/openfang-cli/src/tui/screens/init_wizard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ const PROVIDERS: &[ProviderInfo] = &[
needs_key: true,
hint: "",
},
ProviderInfo {
name: "volcengine",
display: "Volcano Engine",
env_var: "VOLCENGINE_API_KEY",
default_model: "doubao-seed-1-6-251015",
needs_key: true,
hint: "ByteDance Ark platform; cn-beijing; override base_url for other regions",
},
ProviderInfo {
name: "volcengine_coding",
display: "Volcano Engine (Coding Plan)",
env_var: "VOLCENGINE_API_KEY",
default_model: "ark-code-latest",
needs_key: true,
hint: "Shares VOLCENGINE_API_KEY with Volcano Engine standard plan. Uses Ark Coding endpoint.",
},
ProviderInfo {
name: "openrouter",
display: "OpenRouter",
Expand Down Expand Up @@ -143,15 +159,15 @@ const PROVIDERS: &[ProviderInfo] = &[
ProviderInfo {
name: "qwen",
display: "Qwen (Alibaba)",
env_var: "QWEN_API_KEY",
env_var: "DASHSCOPE_API_KEY",
default_model: "qwen-plus",
needs_key: true,
hint: "",
},
ProviderInfo {
name: "huggingface",
display: "Hugging Face",
env_var: "HUGGINGFACE_API_KEY",
env_var: "HF_API_KEY",
default_model: "meta-llama/Llama-3.3-70B-Instruct",
needs_key: true,
hint: "",
Expand All @@ -167,7 +183,7 @@ const PROVIDERS: &[ProviderInfo] = &[
ProviderInfo {
name: "replicate",
display: "Replicate",
env_var: "REPLICATE_API_KEY",
env_var: "REPLICATE_API_TOKEN",
default_model: "meta/meta-llama-3-70b-instruct",
needs_key: true,
hint: "",
Expand Down
3 changes: 2 additions & 1 deletion crates/openfang-cli/src/tui/screens/welcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const PROVIDER_ENV_VARS: &[(&str, &str)] = &[
("ANTHROPIC_API_KEY", "Anthropic"),
("OPENAI_API_KEY", "OpenAI"),
("DEEPSEEK_API_KEY", "DeepSeek"),
("VOLCENGINE_API_KEY", "Volcano Engine"),
("GEMINI_API_KEY", "Gemini"),
("GOOGLE_API_KEY", "Gemini"),
("GROQ_API_KEY", "Groq"),
Expand All @@ -47,7 +48,7 @@ const PROVIDER_ENV_VARS: &[(&str, &str)] = &[
/// Returns (provider_name, env_var_name) for the first detected key, or None.
fn detect_provider() -> Option<(&'static str, &'static str)> {
for &(var, name) in PROVIDER_ENV_VARS {
if std::env::var(var).is_ok() {
if std::env::var(var).ok().filter(|v| !v.is_empty()).is_some() {
return Some((name, var));
}
}
Expand Down
41 changes: 39 additions & 2 deletions crates/openfang-cli/src/tui/screens/wizard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::tui::theme;
/// Provider metadata for the setup wizard.
struct ProviderInfo {
name: &'static str,
display: &'static str,
env_var: &'static str,
default_model: &'static str,
needs_key: bool,
Expand All @@ -21,108 +22,140 @@ struct ProviderInfo {
const PROVIDERS: &[ProviderInfo] = &[
ProviderInfo {
name: "groq",
display: "Groq",
env_var: "GROQ_API_KEY",
default_model: "llama-3.3-70b-versatile",
needs_key: true,
},
ProviderInfo {
name: "anthropic",
display: "Anthropic",
env_var: "ANTHROPIC_API_KEY",
default_model: "claude-sonnet-4-20250514",
needs_key: true,
},
ProviderInfo {
name: "openai",
display: "OpenAI",
env_var: "OPENAI_API_KEY",
default_model: "gpt-4o",
needs_key: true,
},
ProviderInfo {
name: "openrouter",
display: "OpenRouter",
env_var: "OPENROUTER_API_KEY",
default_model: "google/gemini-2.5-flash",
needs_key: true,
},
ProviderInfo {
name: "deepseek",
display: "DeepSeek",
env_var: "DEEPSEEK_API_KEY",
default_model: "deepseek-chat",
needs_key: true,
},
ProviderInfo {
name: "together",
display: "Together AI",
env_var: "TOGETHER_API_KEY",
default_model: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
needs_key: true,
},
ProviderInfo {
name: "mistral",
display: "Mistral",
env_var: "MISTRAL_API_KEY",
default_model: "mistral-large-latest",
needs_key: true,
},
ProviderInfo {
name: "fireworks",
display: "Fireworks AI",
env_var: "FIREWORKS_API_KEY",
default_model: "accounts/fireworks/models/llama-v3p3-70b-instruct",
needs_key: true,
},
ProviderInfo {
name: "gemini",
display: "Gemini",
env_var: "GEMINI_API_KEY",
default_model: "gemini-2.5-flash",
needs_key: true,
},
ProviderInfo {
name: "xai",
display: "xAI",
env_var: "XAI_API_KEY",
default_model: "grok-4-0709",
needs_key: true,
},
ProviderInfo {
name: "qwen",
display: "Qwen",
env_var: "DASHSCOPE_API_KEY",
default_model: "qwen-plus",
needs_key: true,
},
ProviderInfo {
name: "volcengine",
display: "Volcano Engine",
env_var: "VOLCENGINE_API_KEY",
default_model: "doubao-seed-1-6-251015",
needs_key: true,
},
ProviderInfo {
name: "volcengine_coding",
display: "Volcano Engine (Coding Plan)",
env_var: "VOLCENGINE_API_KEY",
default_model: "ark-code-latest",
needs_key: true,
},
ProviderInfo {
name: "perplexity",
display: "Perplexity",
env_var: "PERPLEXITY_API_KEY",
default_model: "sonar-pro",
needs_key: true,
},
ProviderInfo {
name: "cohere",
env_var: "CO_API_KEY",
display: "Cohere",
env_var: "COHERE_API_KEY",
default_model: "command-a",
needs_key: true,
},
ProviderInfo {
name: "cerebras",
display: "Cerebras",
env_var: "CEREBRAS_API_KEY",
default_model: "llama-3.3-70b",
needs_key: true,
},
ProviderInfo {
name: "sambanova",
display: "SambaNova",
env_var: "SAMBANOVA_API_KEY",
default_model: "Meta-Llama-3.3-70B-Instruct",
needs_key: true,
},
ProviderInfo {
name: "moonshot",
display: "Moonshot",
env_var: "MOONSHOT_API_KEY",
default_model: "moonshot-v1-128k",
needs_key: true,
},
ProviderInfo {
name: "zhipu",
display: "Zhipu AI",
env_var: "ZHIPU_API_KEY",
default_model: "glm-4-plus",
needs_key: true,
},
ProviderInfo {
name: "zhipu_coding",
display: "Zhipu AI (Coding)",
env_var: "ZHIPU_API_KEY",
default_model: "codegeex-4",
needs_key: true,
Expand All @@ -135,24 +168,28 @@ const PROVIDERS: &[ProviderInfo] = &[
},
ProviderInfo {
name: "claude-code",
display: "Claude Code",
env_var: "",
default_model: "claude-code/sonnet",
needs_key: false,
},
ProviderInfo {
name: "ollama",
display: "Ollama",
env_var: "OLLAMA_API_KEY",
default_model: "llama3.2",
needs_key: false,
},
ProviderInfo {
name: "vllm",
display: "vLLM",
env_var: "VLLM_API_KEY",
default_model: "local-model",
needs_key: false,
},
ProviderInfo {
name: "lmstudio",
display: "LM Studio",
env_var: "LMSTUDIO_API_KEY",
default_model: "local-model",
needs_key: false,
Expand Down Expand Up @@ -544,7 +581,7 @@ fn draw_provider(f: &mut Frame, area: Rect, state: &mut WizardState) {
format!("requires {}", p.env_var)
};
ListItem::new(Line::from(vec![
Span::raw(format!(" {:<14}", p.name)),
Span::raw(format!(" {:<24}", p.display)),
Span::styled(hint, theme::dim_style()),
]))
})
Expand Down
Loading