mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
Merge remote-tracking branch 'pr-434/fix-provider-model-test-compat' into aether-rust-pioneer
This commit is contained in:
@@ -43,7 +43,8 @@ pub struct AdminEmailTemplateUpdate {
|
||||
}
|
||||
|
||||
pub const ADMIN_SYSTEM_CONFIG_EXPORT_VERSION: &str = "2.2";
|
||||
pub const ADMIN_SYSTEM_CONFIG_SUPPORTED_VERSIONS: &[&str] = &[ADMIN_SYSTEM_CONFIG_EXPORT_VERSION];
|
||||
pub const ADMIN_SYSTEM_CONFIG_SUPPORTED_VERSIONS: &[&str] =
|
||||
&["2.0", "2.1", ADMIN_SYSTEM_CONFIG_EXPORT_VERSION];
|
||||
pub const ADMIN_SYSTEM_USERS_EXPORT_VERSION: &str = "1.4";
|
||||
pub const ADMIN_SYSTEM_USERS_SUPPORTED_VERSIONS: &[&str] =
|
||||
&["1.3", ADMIN_SYSTEM_USERS_EXPORT_VERSION];
|
||||
@@ -78,6 +79,21 @@ fn invalid_request(detail: impl Into<String>) -> (http::StatusCode, serde_json::
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_finite_f64_import_value<E>(raw: &str) -> Result<f64, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let value = raw
|
||||
.trim()
|
||||
.parse::<f64>()
|
||||
.map_err(|_| E::custom("expected a finite number or numeric string"))?;
|
||||
if value.is_finite() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(E::custom("expected a finite number or numeric string"))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_optional_f64_from_number<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
@@ -89,8 +105,13 @@ where
|
||||
.as_f64()
|
||||
.filter(|value| value.is_finite())
|
||||
.map(Some)
|
||||
.ok_or_else(|| de::Error::custom("expected a finite number")),
|
||||
Some(_) => Err(de::Error::custom("expected a finite number")),
|
||||
.ok_or_else(|| de::Error::custom("expected a finite number or numeric string")),
|
||||
Some(Value::String(raw)) if !raw.trim().is_empty() => {
|
||||
parse_finite_f64_import_value::<D::Error>(&raw).map(Some)
|
||||
}
|
||||
Some(_) => Err(de::Error::custom(
|
||||
"expected a finite number or numeric string",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2363,30 +2384,29 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_admin_system_config_import_request_accepts_supported_versions() {
|
||||
let parsed = parse_admin_system_config_import_request(
|
||||
json!({
|
||||
"version": ADMIN_SYSTEM_CONFIG_EXPORT_VERSION,
|
||||
"global_models": [],
|
||||
"providers": [],
|
||||
})
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("current version should parse");
|
||||
for version in ADMIN_SYSTEM_CONFIG_SUPPORTED_VERSIONS {
|
||||
let parsed = parse_admin_system_config_import_request(
|
||||
json!({
|
||||
"version": version,
|
||||
"global_models": [],
|
||||
"providers": [],
|
||||
})
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("supported version should parse");
|
||||
|
||||
assert_eq!(
|
||||
parsed.request.document.version,
|
||||
ADMIN_SYSTEM_CONFIG_EXPORT_VERSION
|
||||
);
|
||||
assert_eq!(parsed.request.merge_mode, AdminImportMergeMode::Skip);
|
||||
assert!(parsed.request.document.oauth_providers.is_empty());
|
||||
assert!(parsed.request.document.system_configs.is_empty());
|
||||
assert!(parsed.request.document.ldap_config.is_none());
|
||||
assert_eq!(parsed.request.document.version, *version);
|
||||
assert_eq!(parsed.request.merge_mode, AdminImportMergeMode::Skip);
|
||||
assert!(parsed.request.document.oauth_providers.is_empty());
|
||||
assert!(parsed.request.document.system_configs.is_empty());
|
||||
assert!(parsed.request.document.ldap_config.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_admin_system_config_import_request_rejects_removed_versions() {
|
||||
for version in ["2.0", "2.1"] {
|
||||
fn parse_admin_system_config_import_request_rejects_unknown_versions() {
|
||||
for version in ["1.9", "2.3"] {
|
||||
let err = parse_admin_system_config_import_request(
|
||||
json!({
|
||||
"version": version,
|
||||
@@ -2396,7 +2416,7 @@ mod tests {
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect_err("removed versions should fail");
|
||||
.expect_err("unknown versions should fail");
|
||||
|
||||
assert_eq!(err.0, http::StatusCode::BAD_REQUEST);
|
||||
assert_eq!(
|
||||
@@ -2455,8 +2475,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_admin_system_config_import_request_rejects_numeric_string_fields() {
|
||||
let err = parse_admin_system_config_import_request(
|
||||
fn parse_admin_system_config_import_request_accepts_numeric_string_fields() {
|
||||
let parsed = parse_admin_system_config_import_request(
|
||||
json!({
|
||||
"version": "2.2",
|
||||
"global_models": [{
|
||||
@@ -2479,7 +2499,34 @@ mod tests {
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect_err("numeric string fields should fail");
|
||||
.expect("numeric string fields from Python exports should parse");
|
||||
|
||||
let global_model = &parsed.request.document.global_models[0];
|
||||
assert_eq!(global_model.default_price_per_request, Some(1.8));
|
||||
|
||||
let provider = &parsed.request.document.providers[0];
|
||||
assert_eq!(provider.monthly_quota_usd, Some(12.5));
|
||||
assert_eq!(provider.stream_first_byte_timeout, Some(60.0));
|
||||
assert_eq!(provider.request_timeout, Some(120.0));
|
||||
assert_eq!(provider.models[0].price_per_request, Some(0.7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_admin_system_config_import_request_rejects_invalid_numeric_string_fields() {
|
||||
let err = parse_admin_system_config_import_request(
|
||||
json!({
|
||||
"version": "2.2",
|
||||
"global_models": [{
|
||||
"name": "veo3.1",
|
||||
"display_name": "Veo 3.1",
|
||||
"default_price_per_request": "not-a-number",
|
||||
}],
|
||||
"providers": [],
|
||||
})
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect_err("invalid numeric string fields should fail");
|
||||
|
||||
assert_eq!(err.0, http::StatusCode::BAD_REQUEST);
|
||||
let detail = err.1["detail"].as_str().expect("detail should be a string");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde_json::Map;
|
||||
use serde_json::Value;
|
||||
use serde_json::{json, Map};
|
||||
|
||||
use crate::formats::context::FormatContext;
|
||||
use crate::formats::openai::embedding::request::mapped_embedding_model;
|
||||
@@ -24,7 +24,7 @@ pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||
Value::Array(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|text| json!({"type": "text", "text": text}))
|
||||
.map(|text| Value::String(text.to_string()))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -239,11 +239,11 @@ mod tests {
|
||||
"openai:embedding",
|
||||
"doubao:embedding",
|
||||
&body,
|
||||
&FormatContext::default().with_mapped_model("doubao-embedding-vision"),
|
||||
&FormatContext::default().with_mapped_model("doubao-embedding-text-240515"),
|
||||
)
|
||||
.expect("doubao embedding conversion should succeed");
|
||||
assert_eq!(doubao["model"], "doubao-embedding-vision");
|
||||
assert_eq!(doubao["input"][0], json!({"type": "text", "text": "alpha"}));
|
||||
assert_eq!(doubao["model"], "doubao-embedding-text-240515");
|
||||
assert_eq!(doubao["input"], json!(["alpha", "beta"]));
|
||||
assert!(doubao.get("messages").is_none());
|
||||
}
|
||||
|
||||
|
||||
@@ -4758,10 +4758,13 @@ mod tests {
|
||||
);
|
||||
assert!(gemini.get("messages").is_none());
|
||||
|
||||
let doubao =
|
||||
super::canonical_to_embedding_request(&canonical, "doubao-embedding-vision", "doubao")
|
||||
.expect("doubao embedding request");
|
||||
assert_eq!(doubao["input"][0], json!({"type": "text", "text": "alpha"}));
|
||||
let doubao = super::canonical_to_embedding_request(
|
||||
&canonical,
|
||||
"doubao-embedding-text-240515",
|
||||
"doubao",
|
||||
)
|
||||
.expect("doubao embedding request");
|
||||
assert_eq!(doubao["input"], json!(["alpha", "beta"]));
|
||||
assert!(doubao.get("messages").is_none());
|
||||
}
|
||||
|
||||
@@ -4802,11 +4805,14 @@ mod tests {
|
||||
assert_eq!(gemini["content"]["parts"][0]["text"], "alpha");
|
||||
assert!(gemini.get("requests").is_none());
|
||||
|
||||
let doubao =
|
||||
super::canonical_to_embedding_request(&single, "doubao-embedding-vision", "doubao")
|
||||
.expect("doubao embedding request");
|
||||
assert_eq!(doubao["model"], "doubao-embedding-vision");
|
||||
assert_eq!(doubao["input"], json!([{"type": "text", "text": "alpha"}]));
|
||||
let doubao = super::canonical_to_embedding_request(
|
||||
&single,
|
||||
"doubao-embedding-text-240515",
|
||||
"doubao",
|
||||
)
|
||||
.expect("doubao embedding request");
|
||||
assert_eq!(doubao["model"], "doubao-embedding-text-240515");
|
||||
assert_eq!(doubao["input"], json!(["alpha"]));
|
||||
assert_eq!(doubao["dimensions"], 1536);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ pub fn build_transport_request_url(
|
||||
),
|
||||
"doubao:embedding" => build_passthrough_path_url(
|
||||
&transport.endpoint.base_url,
|
||||
"/embeddings/multimodal",
|
||||
"/embeddings",
|
||||
params.request_query,
|
||||
&[],
|
||||
),
|
||||
@@ -818,14 +818,14 @@ mod tests {
|
||||
&doubao,
|
||||
TransportRequestUrlParams {
|
||||
provider_api_format: "doubao:embedding",
|
||||
mapped_model: Some("doubao-embedding-vision"),
|
||||
mapped_model: Some("doubao-embedding-text-240515"),
|
||||
upstream_is_stream: false,
|
||||
request_query: None,
|
||||
kiro_api_region: None,
|
||||
},
|
||||
)
|
||||
.as_deref(),
|
||||
Some("https://ark.volces.example/api/v3/embeddings/multimodal")
|
||||
Some("https://ark.volces.example/api/v3/embeddings")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -943,14 +943,14 @@ mod tests {
|
||||
&doubao_with_query,
|
||||
TransportRequestUrlParams {
|
||||
provider_api_format: "doubao:embedding",
|
||||
mapped_model: Some("doubao-embedding-vision"),
|
||||
mapped_model: Some("doubao-embedding-text-240515"),
|
||||
upstream_is_stream: false,
|
||||
request_query: Some("trace=4"),
|
||||
kiro_api_region: None,
|
||||
},
|
||||
)
|
||||
.as_deref(),
|
||||
Some("https://ark.volces.example/api/v3/embeddings/multimodal?tenant=base&trace=4")
|
||||
Some("https://ark.volces.example/api/v3/embeddings?tenant=base&trace=4")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user