Skip to content

Commit ee04701

Browse files
committed
fix(api): provider endpoint mismatches preventing Copilot save, test, and remove
Fix #415 Three endpoint mismatches between frontend and backend caused the GitHub Copilot provider's save, test, and remove buttons to fail with 405 Method Not Allowed. These affected all providers for save. - change update_provider annotation from post to put to match frontend - fix test button URL from /providers/test to /providers/test-model - add github-copilot entry in build_test_llm_config since default_provider_config returns None for providers that require token exchange - widen GITHUB_COPILOT_DEFAULT_BASE_URL visibility to pub(crate) - add unit test for build_test_llm_config with github-copilot fix(api): Copilot provider shows as available after remove when env var is set get_providers fell back to the GITHUB_COPILOT_API_KEY env var when the TOML key was absent, so the provider stayed visible in settings after a remove — the env var can't be unset from a running process. Only check the TOML key for Copilot status in the config-exists path. The env var fallback remains for the no-config-file case (fresh install).
1 parent fb5c0f3 commit ee04701

3 files changed

Lines changed: 49 additions & 2 deletions

File tree

src/api/providers.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,23 @@ fn build_test_llm_config(provider: &str, credential: &str) -> crate::config::Llm
207207
let mut providers = HashMap::new();
208208
if let Some(provider_config) = crate::config::default_provider_config(provider, credential) {
209209
providers.insert(provider.to_string(), provider_config);
210+
} else if provider == "github-copilot" {
211+
// GitHub Copilot uses token exchange, so default_provider_config returns None.
212+
// For testing, add a provider entry with the PAT as the api_key —
213+
// LlmManager::get_copilot_token() will exchange it for a real Copilot token.
214+
providers.insert(
215+
provider.to_string(),
216+
crate::config::ProviderConfig {
217+
api_type: crate::config::ApiType::OpenAiChatCompletions,
218+
base_url: crate::config::GITHUB_COPILOT_DEFAULT_BASE_URL.to_string(),
219+
api_key: credential.to_string(),
220+
name: Some("GitHub Copilot".to_string()),
221+
use_bearer_auth: true,
222+
extra_headers: vec![],
223+
api_version: None,
224+
deployment: None,
225+
},
226+
);
210227
}
211228

212229
crate::config::LlmConfig {
@@ -447,6 +464,15 @@ pub(super) async fn get_providers(
447464
env_set(env_var)
448465
};
449466

467+
// Copilot: only check TOML key, not env var. The env var GITHUB_COPILOT_API_KEY
468+
// is a fallback for config loading but shouldn't keep the provider visible in
469+
// the settings UI after a remove — the env var can't be unset from the process.
470+
let copilot_configured = doc
471+
.get("llm")
472+
.and_then(|llm| llm.get("github_copilot_key"))
473+
.and_then(|val| val.as_str())
474+
.is_some_and(|s| resolve_value(s).is_some());
475+
450476
(
451477
has_value("anthropic_key", "ANTHROPIC_API_KEY"),
452478
has_value("openai_key", "OPENAI_API_KEY"),
@@ -470,7 +496,7 @@ pub(super) async fn get_providers(
470496
has_value("minimax_cn_key", "MINIMAX_CN_API_KEY"),
471497
has_value("moonshot_key", "MOONSHOT_API_KEY"),
472498
has_value("zai_coding_plan_key", "ZAI_CODING_PLAN_API_KEY"),
473-
has_value("github_copilot_key", "GITHUB_COPILOT_API_KEY"),
499+
copilot_configured,
474500
doc.get("llm")
475501
.and_then(|llm| llm.get("provider"))
476502
.and_then(|provider| provider.get("azure"))
@@ -1607,4 +1633,24 @@ mod tests {
16071633
assert_eq!(provider.base_url, "http://remote-ollama.local:11434");
16081634
assert_eq!(provider.api_key, "");
16091635
}
1636+
1637+
#[test]
1638+
fn build_test_llm_config_registers_github_copilot_provider() {
1639+
let config = build_test_llm_config("github-copilot", "ghp_test_pat_token");
1640+
let provider = config
1641+
.providers
1642+
.get("github-copilot")
1643+
.expect("github-copilot provider should be registered");
1644+
1645+
assert_eq!(
1646+
provider.base_url,
1647+
crate::config::GITHUB_COPILOT_DEFAULT_BASE_URL
1648+
);
1649+
assert_eq!(provider.api_key, "ghp_test_pat_token");
1650+
assert!(provider.use_bearer_auth);
1651+
assert_eq!(
1652+
config.github_copilot_key.as_deref(),
1653+
Some("ghp_test_pat_token")
1654+
);
1655+
}
16101656
}

src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use permissions::{
1818
DiscordPermissions, MattermostPermissions, SignalPermissions, SlackPermissions,
1919
TelegramPermissions, TwitchPermissions,
2020
};
21+
pub(crate) use providers::GITHUB_COPILOT_DEFAULT_BASE_URL;
2122
pub(crate) use providers::default_provider_config;
2223
pub use runtime::RuntimeConfig;
2324
pub use types::*;

src/config/providers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub(super) const NVIDIA_PROVIDER_BASE_URL: &str = "https://integrate.api.nvidia.
2626
pub(super) const FIREWORKS_PROVIDER_BASE_URL: &str = "https://api.fireworks.ai/inference";
2727
pub(crate) const GEMINI_PROVIDER_BASE_URL: &str =
2828
"https://generativelanguage.googleapis.com/v1beta/openai";
29-
pub(super) const GITHUB_COPILOT_DEFAULT_BASE_URL: &str = "https://api.individual.githubcopilot.com";
29+
pub(crate) const GITHUB_COPILOT_DEFAULT_BASE_URL: &str = "https://api.individual.githubcopilot.com";
3030

3131
/// App attribution headers sent with every OpenRouter API request.
3232
/// See <https://openrouter.ai/docs/app-attribution>.

0 commit comments

Comments
 (0)