|
|
|
@ -22,7 +22,8 @@ pub struct AdminProvider {
|
|
|
|
pub id: Uuid,
|
|
|
|
pub id: Uuid,
|
|
|
|
pub provider_name: String,
|
|
|
|
pub provider_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub models: Vec<ProviderModel>,
|
|
|
|
pub models_scraping: Vec<ProviderModel>,
|
|
|
|
|
|
|
|
pub models_websearch: Vec<ProviderModel>,
|
|
|
|
pub is_enabled: bool,
|
|
|
|
pub is_enabled: bool,
|
|
|
|
pub created_at: DateTime<Utc>,
|
|
|
|
pub created_at: DateTime<Utc>,
|
|
|
|
pub updated_at: DateTime<Utc>,
|
|
|
|
pub updated_at: DateTime<Utc>,
|
|
|
|
@ -33,7 +34,8 @@ pub struct AdminProvider {
|
|
|
|
pub struct CreateProviderRequest {
|
|
|
|
pub struct CreateProviderRequest {
|
|
|
|
pub provider_name: String,
|
|
|
|
pub provider_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub models: Vec<ProviderModel>,
|
|
|
|
pub models_scraping: Vec<ProviderModel>,
|
|
|
|
|
|
|
|
pub models_websearch: Vec<ProviderModel>,
|
|
|
|
#[serde(default = "default_true")]
|
|
|
|
#[serde(default = "default_true")]
|
|
|
|
pub is_enabled: bool,
|
|
|
|
pub is_enabled: bool,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -69,7 +71,8 @@ impl CreateProviderRequest {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
validate_display_name(&self.display_name)?;
|
|
|
|
validate_display_name(&self.display_name)?;
|
|
|
|
validate_models(&self.models)?;
|
|
|
|
validate_models(&self.models_scraping)?;
|
|
|
|
|
|
|
|
validate_models(&self.models_websearch)?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -79,7 +82,8 @@ impl CreateProviderRequest {
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct UpdateProviderRequest {
|
|
|
|
pub struct UpdateProviderRequest {
|
|
|
|
pub display_name: Option<String>,
|
|
|
|
pub display_name: Option<String>,
|
|
|
|
pub models: Option<Vec<ProviderModel>>,
|
|
|
|
pub models_scraping: Option<Vec<ProviderModel>>,
|
|
|
|
|
|
|
|
pub models_websearch: Option<Vec<ProviderModel>>,
|
|
|
|
pub is_enabled: Option<bool>,
|
|
|
|
pub is_enabled: Option<bool>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -89,7 +93,10 @@ impl UpdateProviderRequest {
|
|
|
|
if let Some(ref display) = self.display_name {
|
|
|
|
if let Some(ref display) = self.display_name {
|
|
|
|
validate_display_name(display)?;
|
|
|
|
validate_display_name(display)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(ref models) = self.models {
|
|
|
|
if let Some(ref models) = self.models_scraping {
|
|
|
|
|
|
|
|
validate_models(models)?;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(ref models) = self.models_websearch {
|
|
|
|
validate_models(models)?;
|
|
|
|
validate_models(models)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
@ -143,7 +150,8 @@ fn validate_models(models: &[ProviderModel]) -> Result<(), String> {
|
|
|
|
pub struct ProviderConfigResponse {
|
|
|
|
pub struct ProviderConfigResponse {
|
|
|
|
pub provider_name: String,
|
|
|
|
pub provider_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub models: Vec<PublicModelInfo>,
|
|
|
|
pub models_scraping: Vec<PublicModelInfo>,
|
|
|
|
|
|
|
|
pub models_websearch: Vec<PublicModelInfo>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Public model info (subset of `ProviderModel`).
|
|
|
|
/// Public model info (subset of `ProviderModel`).
|
|
|
|
@ -170,7 +178,8 @@ pub struct AdminProviderResponse {
|
|
|
|
pub id: Uuid,
|
|
|
|
pub id: Uuid,
|
|
|
|
pub provider_name: String,
|
|
|
|
pub provider_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
pub models: Vec<ProviderModel>,
|
|
|
|
pub models_scraping: Vec<ProviderModel>,
|
|
|
|
|
|
|
|
pub models_websearch: Vec<ProviderModel>,
|
|
|
|
pub is_enabled: bool,
|
|
|
|
pub is_enabled: bool,
|
|
|
|
pub created_at: DateTime<Utc>,
|
|
|
|
pub created_at: DateTime<Utc>,
|
|
|
|
pub updated_at: DateTime<Utc>,
|
|
|
|
pub updated_at: DateTime<Utc>,
|
|
|
|
@ -182,7 +191,8 @@ impl From<AdminProvider> for AdminProviderResponse {
|
|
|
|
id: p.id,
|
|
|
|
id: p.id,
|
|
|
|
provider_name: p.provider_name,
|
|
|
|
provider_name: p.provider_name,
|
|
|
|
display_name: p.display_name,
|
|
|
|
display_name: p.display_name,
|
|
|
|
models: p.models,
|
|
|
|
models_scraping: p.models_scraping,
|
|
|
|
|
|
|
|
models_websearch: p.models_websearch,
|
|
|
|
is_enabled: p.is_enabled,
|
|
|
|
is_enabled: p.is_enabled,
|
|
|
|
created_at: p.created_at,
|
|
|
|
created_at: p.created_at,
|
|
|
|
updated_at: p.updated_at,
|
|
|
|
updated_at: p.updated_at,
|
|
|
|
@ -194,12 +204,26 @@ impl From<AdminProvider> for AdminProviderResponse {
|
|
|
|
mod tests {
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Helper to create a sample model list for tests.
|
|
|
|
|
|
|
|
fn sample_models() -> Vec<ProviderModel> {
|
|
|
|
|
|
|
|
vec![ProviderModel {
|
|
|
|
|
|
|
|
model_id: "m1".into(),
|
|
|
|
|
|
|
|
display_name: "Model 1".into(),
|
|
|
|
|
|
|
|
is_default: true,
|
|
|
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
fn test_valid_create_request() {
|
|
|
|
fn test_valid_create_request() {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
provider_name: "gemini".into(),
|
|
|
|
provider_name: "gemini".into(),
|
|
|
|
display_name: "Google Gemini".into(),
|
|
|
|
display_name: "Google Gemini".into(),
|
|
|
|
models: vec![ProviderModel {
|
|
|
|
models_scraping: vec![ProviderModel {
|
|
|
|
|
|
|
|
model_id: "gemini-2.5-pro".into(),
|
|
|
|
|
|
|
|
display_name: "Gemini 2.5 Pro".into(),
|
|
|
|
|
|
|
|
is_default: true,
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
models_websearch: vec![ProviderModel {
|
|
|
|
model_id: "gemini-2.5-pro".into(),
|
|
|
|
model_id: "gemini-2.5-pro".into(),
|
|
|
|
display_name: "Gemini 2.5 Pro".into(),
|
|
|
|
display_name: "Gemini 2.5 Pro".into(),
|
|
|
|
is_default: true,
|
|
|
|
is_default: true,
|
|
|
|
@ -214,11 +238,8 @@ mod tests {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
provider_name: "unknown_provider".into(),
|
|
|
|
provider_name: "unknown_provider".into(),
|
|
|
|
display_name: "Unknown".into(),
|
|
|
|
display_name: "Unknown".into(),
|
|
|
|
models: vec![ProviderModel {
|
|
|
|
models_scraping: sample_models(),
|
|
|
|
model_id: "m1".into(),
|
|
|
|
models_websearch: sample_models(),
|
|
|
|
display_name: "Model 1".into(),
|
|
|
|
|
|
|
|
is_default: false,
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
is_enabled: true,
|
|
|
|
is_enabled: true,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
@ -230,11 +251,8 @@ mod tests {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
provider_name: " ".into(),
|
|
|
|
provider_name: " ".into(),
|
|
|
|
display_name: "Some Provider".into(),
|
|
|
|
display_name: "Some Provider".into(),
|
|
|
|
models: vec![ProviderModel {
|
|
|
|
models_scraping: sample_models(),
|
|
|
|
model_id: "m1".into(),
|
|
|
|
models_websearch: sample_models(),
|
|
|
|
display_name: "Model 1".into(),
|
|
|
|
|
|
|
|
is_default: false,
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
is_enabled: true,
|
|
|
|
is_enabled: true,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
@ -242,11 +260,25 @@ mod tests {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
fn test_empty_models_list() {
|
|
|
|
fn test_empty_models_scraping_list() {
|
|
|
|
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
|
|
|
|
provider_name: "openai".into(),
|
|
|
|
|
|
|
|
display_name: "OpenAI".into(),
|
|
|
|
|
|
|
|
models_scraping: vec![],
|
|
|
|
|
|
|
|
models_websearch: sample_models(),
|
|
|
|
|
|
|
|
is_enabled: true,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
|
|
|
|
assert!(err.contains("At least one model"));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn test_empty_models_websearch_list() {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
provider_name: "openai".into(),
|
|
|
|
provider_name: "openai".into(),
|
|
|
|
display_name: "OpenAI".into(),
|
|
|
|
display_name: "OpenAI".into(),
|
|
|
|
models: vec![],
|
|
|
|
models_scraping: sample_models(),
|
|
|
|
|
|
|
|
models_websearch: vec![],
|
|
|
|
is_enabled: true,
|
|
|
|
is_enabled: true,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
@ -258,7 +290,7 @@ mod tests {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
provider_name: "openai".into(),
|
|
|
|
provider_name: "openai".into(),
|
|
|
|
display_name: "OpenAI".into(),
|
|
|
|
display_name: "OpenAI".into(),
|
|
|
|
models: vec![
|
|
|
|
models_scraping: vec![
|
|
|
|
ProviderModel {
|
|
|
|
ProviderModel {
|
|
|
|
model_id: "gpt-4o".into(),
|
|
|
|
model_id: "gpt-4o".into(),
|
|
|
|
display_name: "GPT-4o".into(),
|
|
|
|
display_name: "GPT-4o".into(),
|
|
|
|
@ -270,6 +302,7 @@ mod tests {
|
|
|
|
is_default: true,
|
|
|
|
is_default: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
models_websearch: sample_models(),
|
|
|
|
is_enabled: true,
|
|
|
|
is_enabled: true,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
@ -281,11 +314,12 @@ mod tests {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
let req = CreateProviderRequest {
|
|
|
|
provider_name: "anthropic".into(),
|
|
|
|
provider_name: "anthropic".into(),
|
|
|
|
display_name: "Anthropic".into(),
|
|
|
|
display_name: "Anthropic".into(),
|
|
|
|
models: vec![ProviderModel {
|
|
|
|
models_scraping: vec![ProviderModel {
|
|
|
|
model_id: "".into(),
|
|
|
|
model_id: "".into(),
|
|
|
|
display_name: "Claude".into(),
|
|
|
|
display_name: "Claude".into(),
|
|
|
|
is_default: false,
|
|
|
|
is_default: false,
|
|
|
|
}],
|
|
|
|
}],
|
|
|
|
|
|
|
|
models_websearch: sample_models(),
|
|
|
|
is_enabled: true,
|
|
|
|
is_enabled: true,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
@ -296,7 +330,8 @@ mod tests {
|
|
|
|
fn test_update_request_all_none() {
|
|
|
|
fn test_update_request_all_none() {
|
|
|
|
let req = UpdateProviderRequest {
|
|
|
|
let req = UpdateProviderRequest {
|
|
|
|
display_name: None,
|
|
|
|
display_name: None,
|
|
|
|
models: None,
|
|
|
|
models_scraping: None,
|
|
|
|
|
|
|
|
models_websearch: None,
|
|
|
|
is_enabled: None,
|
|
|
|
is_enabled: None,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
assert!(req.validate().is_ok());
|
|
|
|
assert!(req.validate().is_ok());
|
|
|
|
@ -306,7 +341,8 @@ mod tests {
|
|
|
|
fn test_update_request_empty_display_name() {
|
|
|
|
fn test_update_request_empty_display_name() {
|
|
|
|
let req = UpdateProviderRequest {
|
|
|
|
let req = UpdateProviderRequest {
|
|
|
|
display_name: Some("".into()),
|
|
|
|
display_name: Some("".into()),
|
|
|
|
models: None,
|
|
|
|
models_scraping: None,
|
|
|
|
|
|
|
|
models_websearch: None,
|
|
|
|
is_enabled: None,
|
|
|
|
is_enabled: None,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
let err = req.validate().unwrap_err();
|
|
|
|
|