feat(provider): add qwen-code oauth credential support
This commit is contained in:
parent
e9c280324f
commit
bca58acdcb
3 changed files with 501 additions and 6 deletions
|
|
@ -12,7 +12,8 @@ use crate::memory::{
|
|||
};
|
||||
use crate::providers::{
|
||||
canonical_china_provider_name, is_glm_alias, is_glm_cn_alias, is_minimax_alias,
|
||||
is_moonshot_alias, is_qianfan_alias, is_qwen_alias, is_zai_alias, is_zai_cn_alias,
|
||||
is_moonshot_alias, is_qianfan_alias, is_qwen_alias, is_qwen_oauth_alias, is_zai_alias,
|
||||
is_zai_cn_alias,
|
||||
};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use console::style;
|
||||
|
|
@ -497,6 +498,10 @@ pub async fn run_quick_setup(
|
|||
}
|
||||
|
||||
fn canonical_provider_name(provider_name: &str) -> &str {
|
||||
if is_qwen_oauth_alias(provider_name) {
|
||||
return "qwen-code";
|
||||
}
|
||||
|
||||
if let Some(canonical) = canonical_china_provider_name(provider_name) {
|
||||
return canonical;
|
||||
}
|
||||
|
|
@ -547,6 +552,7 @@ fn default_model_for_provider(provider: &str) -> String {
|
|||
"glm" | "zai" => "glm-5".into(),
|
||||
"minimax" => "MiniMax-M2.5".into(),
|
||||
"qwen" => "qwen-plus".into(),
|
||||
"qwen-code" => "qwen3-coder-plus".into(),
|
||||
"ollama" => "llama3.2".into(),
|
||||
"gemini" => "gemini-2.5-pro".into(),
|
||||
"kimi-code" => "kimi-for-coding".into(),
|
||||
|
|
@ -823,6 +829,20 @@ fn curated_models_for_provider(provider_name: &str) -> Vec<(String, String)> {
|
|||
"Qwen Turbo (fast and cost-efficient)".to_string(),
|
||||
),
|
||||
],
|
||||
"qwen-code" => vec![
|
||||
(
|
||||
"qwen3-coder-plus".to_string(),
|
||||
"Qwen3 Coder Plus (recommended for coding workflows)".to_string(),
|
||||
),
|
||||
(
|
||||
"qwen3.5-plus".to_string(),
|
||||
"Qwen3.5 Plus (reasoning + coding)".to_string(),
|
||||
),
|
||||
(
|
||||
"qwen3-max-2026-01-23".to_string(),
|
||||
"Qwen3 Max (high-capability coding model)".to_string(),
|
||||
),
|
||||
],
|
||||
"nvidia" => vec![
|
||||
(
|
||||
"meta/llama-3.3-70b-instruct".to_string(),
|
||||
|
|
@ -1601,6 +1621,10 @@ fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, Optio
|
|||
"kimi-code",
|
||||
"Kimi Code — coding-optimized Kimi API (KimiCLI)",
|
||||
),
|
||||
(
|
||||
"qwen-code",
|
||||
"Qwen Code — OAuth tokens reused from ~/.qwen/oauth_creds.json",
|
||||
),
|
||||
("moonshot", "Moonshot — Kimi API (China endpoint)"),
|
||||
(
|
||||
"moonshot-intl",
|
||||
|
|
@ -1809,11 +1833,48 @@ fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, Optio
|
|||
|
||||
key
|
||||
}
|
||||
} else if canonical_provider_name(provider_name) == "qwen-code" {
|
||||
if std::env::var("QWEN_OAUTH_TOKEN").is_ok() {
|
||||
print_bullet(&format!(
|
||||
"{} QWEN_OAUTH_TOKEN environment variable detected!",
|
||||
style("✓").green().bold()
|
||||
));
|
||||
"qwen-oauth".to_string()
|
||||
} else {
|
||||
print_bullet(
|
||||
"Qwen Code OAuth credentials are usually stored in ~/.qwen/oauth_creds.json.",
|
||||
);
|
||||
print_bullet(
|
||||
"Run `qwen` once and complete OAuth login to populate cached credentials.",
|
||||
);
|
||||
print_bullet("You can also set QWEN_OAUTH_TOKEN directly.");
|
||||
println!();
|
||||
|
||||
let key: String = Input::new()
|
||||
.with_prompt(
|
||||
" Paste your Qwen OAuth token (or press Enter to auto-detect cached OAuth)",
|
||||
)
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
|
||||
if key.trim().is_empty() {
|
||||
print_bullet(&format!(
|
||||
"Using OAuth auto-detection. Set {} and optional {} if needed.",
|
||||
style("QWEN_OAUTH_TOKEN").yellow(),
|
||||
style("QWEN_OAUTH_RESOURCE_URL").yellow()
|
||||
));
|
||||
"qwen-oauth".to_string()
|
||||
} else {
|
||||
key
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let key_url = if is_moonshot_alias(provider_name)
|
||||
|| canonical_provider_name(provider_name) == "kimi-code"
|
||||
{
|
||||
"https://platform.moonshot.cn/console/api-keys"
|
||||
} else if canonical_provider_name(provider_name) == "qwen-code" {
|
||||
"https://qwen.readthedocs.io/en/latest/getting_started/installation.html"
|
||||
} else if is_glm_cn_alias(provider_name) || is_zai_cn_alias(provider_name) {
|
||||
"https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys"
|
||||
} else if is_glm_alias(provider_name) || is_zai_alias(provider_name) {
|
||||
|
|
@ -2066,6 +2127,10 @@ fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, Optio
|
|||
|
||||
/// Map provider name to its conventional env var
|
||||
fn provider_env_var(name: &str) -> &'static str {
|
||||
if canonical_provider_name(name) == "qwen-code" {
|
||||
return "QWEN_OAUTH_TOKEN";
|
||||
}
|
||||
|
||||
match canonical_provider_name(name) {
|
||||
"openrouter" => "OPENROUTER_API_KEY",
|
||||
"anthropic" => "ANTHROPIC_API_KEY",
|
||||
|
|
@ -5044,6 +5109,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(default_model_for_provider("qwen"), "qwen-plus");
|
||||
assert_eq!(default_model_for_provider("qwen-intl"), "qwen-plus");
|
||||
assert_eq!(default_model_for_provider("qwen-code"), "qwen3-coder-plus");
|
||||
assert_eq!(default_model_for_provider("glm-cn"), "glm-5");
|
||||
assert_eq!(default_model_for_provider("minimax-cn"), "MiniMax-M2.5");
|
||||
assert_eq!(default_model_for_provider("zai-cn"), "glm-5");
|
||||
|
|
@ -5078,6 +5144,8 @@ mod tests {
|
|||
fn canonical_provider_name_normalizes_regional_aliases() {
|
||||
assert_eq!(canonical_provider_name("qwen-intl"), "qwen");
|
||||
assert_eq!(canonical_provider_name("dashscope-us"), "qwen");
|
||||
assert_eq!(canonical_provider_name("qwen-code"), "qwen-code");
|
||||
assert_eq!(canonical_provider_name("qwen-oauth"), "qwen-code");
|
||||
assert_eq!(canonical_provider_name("moonshot-intl"), "moonshot");
|
||||
assert_eq!(canonical_provider_name("kimi-cn"), "moonshot");
|
||||
assert_eq!(canonical_provider_name("kimi_coding"), "kimi-code");
|
||||
|
|
@ -5188,6 +5256,18 @@ mod tests {
|
|||
assert!(ids.contains(&"kimi-k2.5".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curated_models_for_qwen_code_include_coding_plan_models() {
|
||||
let ids: Vec<String> = curated_models_for_provider("qwen-code")
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect();
|
||||
|
||||
assert!(ids.contains(&"qwen3-coder-plus".to_string()));
|
||||
assert!(ids.contains(&"qwen3.5-plus".to_string()));
|
||||
assert!(ids.contains(&"qwen3-max-2026-01-23".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supports_live_model_fetch_for_supported_and_unsupported_providers() {
|
||||
assert!(supports_live_model_fetch("openai"));
|
||||
|
|
@ -5459,6 +5539,8 @@ mod tests {
|
|||
assert_eq!(provider_env_var("qwen"), "DASHSCOPE_API_KEY");
|
||||
assert_eq!(provider_env_var("qwen-intl"), "DASHSCOPE_API_KEY");
|
||||
assert_eq!(provider_env_var("dashscope-us"), "DASHSCOPE_API_KEY");
|
||||
assert_eq!(provider_env_var("qwen-code"), "QWEN_OAUTH_TOKEN");
|
||||
assert_eq!(provider_env_var("qwen-oauth"), "QWEN_OAUTH_TOKEN");
|
||||
assert_eq!(provider_env_var("glm-cn"), "GLM_API_KEY");
|
||||
assert_eq!(provider_env_var("minimax-cn"), "MINIMAX_API_KEY");
|
||||
assert_eq!(provider_env_var("kimi-code"), "KIMI_CODE_API_KEY");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue