Onboarding detect provider from api key (#5955)
Co-authored-by: spencrmartin <spencermartin@squareup.com> Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
@@ -330,6 +330,7 @@ derive_utoipa!(Icon as IconSchema);
|
||||
super::routes::status::diagnostics,
|
||||
super::routes::mcp_ui_proxy::mcp_ui_proxy,
|
||||
super::routes::config_management::backup_config,
|
||||
super::routes::config_management::detect_provider,
|
||||
super::routes::config_management::recover_config,
|
||||
super::routes::config_management::validate_config,
|
||||
super::routes::config_management::init_config,
|
||||
@@ -398,6 +399,8 @@ derive_utoipa!(Icon as IconSchema);
|
||||
components(schemas(
|
||||
super::routes::config_management::UpsertConfigQuery,
|
||||
super::routes::config_management::ConfigKeyQuery,
|
||||
super::routes::config_management::DetectProviderRequest,
|
||||
super::routes::config_management::DetectProviderResponse,
|
||||
super::routes::config_management::ConfigResponse,
|
||||
super::routes::config_management::ProvidersResponse,
|
||||
super::routes::config_management::ProviderDetails,
|
||||
|
||||
@@ -11,6 +11,7 @@ use goose::config::paths::Paths;
|
||||
use goose::config::ExtensionEntry;
|
||||
use goose::config::{Config, ConfigError};
|
||||
use goose::model::ModelConfig;
|
||||
use goose::providers::auto_detect::detect_provider_from_api_key;
|
||||
use goose::providers::base::{ProviderMetadata, ProviderType};
|
||||
use goose::providers::create_with_default_model;
|
||||
use goose::providers::pricing::{
|
||||
@@ -131,6 +132,16 @@ pub struct SlashCommandsResponse {
|
||||
pub commands: Vec<SlashCommand>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct DetectProviderRequest {
|
||||
pub api_key: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct DetectProviderResponse {
|
||||
pub provider_name: String,
|
||||
pub models: Vec<String>,
|
||||
}
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/config/upsert",
|
||||
@@ -596,6 +607,29 @@ pub async fn upsert_permissions(
|
||||
Ok(Json("Permissions updated successfully".to_string()))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/config/detect-provider",
|
||||
request_body = DetectProviderRequest,
|
||||
responses(
|
||||
(status = 200, description = "Provider detected successfully", body = DetectProviderResponse),
|
||||
(status = 404, description = "No matching provider found"),
|
||||
)
|
||||
)]
|
||||
pub async fn detect_provider(
|
||||
Json(detect_request): Json<DetectProviderRequest>,
|
||||
) -> Result<Json<DetectProviderResponse>, StatusCode> {
|
||||
let api_key = detect_request.api_key.trim();
|
||||
|
||||
match detect_provider_from_api_key(api_key).await {
|
||||
Some((provider_name, models)) => Ok(Json(DetectProviderResponse {
|
||||
provider_name,
|
||||
models,
|
||||
})),
|
||||
None => Err(StatusCode::NOT_FOUND),
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/config/backup",
|
||||
@@ -686,7 +720,6 @@ pub async fn validate_config() -> Result<Json<String>, StatusCode> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/config/custom-providers",
|
||||
@@ -834,6 +867,7 @@ pub fn routes(state: Arc<AppState>) -> Router {
|
||||
.route("/config/extensions/{name}", delete(remove_extension))
|
||||
.route("/config/providers", get(providers))
|
||||
.route("/config/providers/{name}/models", get(get_provider_models))
|
||||
.route("/config/detect-provider", post(detect_provider))
|
||||
.route("/config/slash_commands", get(get_slash_commands))
|
||||
.route("/config/pricing", post(get_pricing))
|
||||
.route("/config/init", post(init_config))
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
use crate::model::ModelConfig;
|
||||
|
||||
pub async fn detect_provider_from_api_key(api_key: &str) -> Option<(String, Vec<String>)> {
|
||||
let provider_tests = vec![
|
||||
("anthropic", "ANTHROPIC_API_KEY"),
|
||||
("openai", "OPENAI_API_KEY"),
|
||||
("google", "GOOGLE_API_KEY"),
|
||||
("groq", "GROQ_API_KEY"),
|
||||
("xai", "XAI_API_KEY"),
|
||||
// Ollama and OpenRouter don't validate keys, so they would match any input
|
||||
];
|
||||
|
||||
let tasks: Vec<_> = provider_tests
|
||||
.into_iter()
|
||||
.map(|(provider_name, env_key)| {
|
||||
let api_key = api_key.to_string();
|
||||
tokio::spawn(async move {
|
||||
let original_value = std::env::var(env_key).ok();
|
||||
std::env::set_var(env_key, &api_key);
|
||||
|
||||
let result = match crate::providers::create(
|
||||
provider_name,
|
||||
ModelConfig::new_or_fail("default"),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(provider) => match provider.fetch_supported_models().await {
|
||||
Ok(Some(models)) => Some((provider_name.to_string(), models)),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
match original_value {
|
||||
Some(val) => std::env::set_var(env_key, val),
|
||||
None => std::env::remove_var(env_key),
|
||||
}
|
||||
|
||||
result
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
for task in tasks {
|
||||
if let Ok(Some(result)) = task.await {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod anthropic;
|
||||
pub mod api_client;
|
||||
pub mod auto_detect;
|
||||
pub mod azure;
|
||||
pub mod azureauth;
|
||||
pub mod base;
|
||||
|
||||
Reference in New Issue
Block a user