mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
fix(provider): 修复 Windsurf PR CI 失败
This commit is contained in:
@@ -934,7 +934,11 @@ async fn resolve_openai_chat_to_openai_image_payload_parts(
|
||||
let upstream_url = if is_chatgpt_web {
|
||||
chatgpt_web_image_internal_url(&transport.endpoint.base_url)
|
||||
} else {
|
||||
build_openai_image_upstream_url(transport, parts.uri.query())
|
||||
build_openai_image_upstream_url(
|
||||
transport,
|
||||
Some("/v1/images/generations"),
|
||||
parts.uri.query(),
|
||||
)
|
||||
};
|
||||
let Some(mut provider_request_headers) =
|
||||
build_openai_image_headers(ProviderOpenAiImageHeadersInput {
|
||||
|
||||
@@ -1727,7 +1727,7 @@ async fn execute_execution_runtime_sync_impl(
|
||||
}
|
||||
let (mut result_error_type, mut result_error_message) =
|
||||
execution_error_details(result.error.as_ref(), body_json.as_ref());
|
||||
if result.status_code < 400 {
|
||||
if result.status_code < 400 && body_json.is_none() {
|
||||
if let Some(error_body_json) =
|
||||
extract_provider_private_stream_error_body(report_context.as_ref(), &body_bytes)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ use serde_json::json;
|
||||
use serde_json::Value;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::ai_serving::api::extract_provider_private_stream_error_body;
|
||||
#[cfg(test)]
|
||||
use crate::execution_runtime::remote_compat::execute_sync_plan_via_remote_execution_runtime;
|
||||
use crate::execution_runtime::windsurf::maybe_execute_windsurf_sync;
|
||||
@@ -1466,11 +1467,8 @@ pub(crate) fn build_execution_response_body(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if let Some(body_json) =
|
||||
aether_ai_formats::api::extract_provider_private_stream_error_body(None, decoded_body_bytes)
|
||||
.or_else(|| {
|
||||
aether_ai_formats::api::extract_provider_private_stream_error_body(None, body_bytes)
|
||||
})
|
||||
if let Some(body_json) = extract_provider_private_stream_error_body(None, decoded_body_bytes)
|
||||
.or_else(|| extract_provider_private_stream_error_body(None, body_bytes))
|
||||
{
|
||||
return Ok(Some(ResponseBody {
|
||||
json_body: Some(body_json),
|
||||
|
||||
@@ -345,7 +345,6 @@ async fn prepare_windsurf_cascade(
|
||||
native_allowlist: native_bridge
|
||||
.map(|bridge| bridge.native_allowlist.clone())
|
||||
.unwrap_or_default(),
|
||||
..SendCascadeMessageOptions::default()
|
||||
};
|
||||
let send_payload = build_send_cascade_message_request_with_options(
|
||||
&input.api_key,
|
||||
@@ -414,7 +413,7 @@ async fn read_windsurf_key_upstream_metadata(
|
||||
}
|
||||
|
||||
match state
|
||||
.read_provider_catalog_keys_by_ids(&[plan.key_id.clone()])
|
||||
.read_provider_catalog_keys_by_ids(std::slice::from_ref(&plan.key_id))
|
||||
.await
|
||||
{
|
||||
Ok(keys) => keys
|
||||
@@ -1693,10 +1692,8 @@ fn build_cascade_message_with_options(
|
||||
};
|
||||
let text = text.trim();
|
||||
match role {
|
||||
"system" => {
|
||||
if !text.is_empty() {
|
||||
system_text.push(text.to_string());
|
||||
}
|
||||
"system" if !text.is_empty() => {
|
||||
system_text.push(text.to_string());
|
||||
}
|
||||
"assistant" => {
|
||||
let assistant_text = assistant_message_text_for_cascade(message, text, dialect);
|
||||
|
||||
@@ -25,6 +25,7 @@ use crate::handlers::admin::provider::oauth::runtime::{
|
||||
use crate::handlers::admin::provider::oauth::state::{
|
||||
admin_provider_oauth_template, exchange_admin_provider_oauth_refresh_token,
|
||||
};
|
||||
use crate::handlers::admin::provider::shared::support::ADMIN_PROVIDER_OAUTH_DATA_UNAVAILABLE_DETAIL;
|
||||
use crate::handlers::admin::request::{AdminAppState, AdminProviderOAuthTemplate};
|
||||
use crate::GatewayError;
|
||||
use aether_admin::provider::oauth::parse_admin_provider_oauth_kiro_batch_import_entries;
|
||||
@@ -167,10 +168,6 @@ async fn resolve_admin_provider_oauth_batch_import_tokens(
|
||||
});
|
||||
}
|
||||
|
||||
let Some(template) = template else {
|
||||
return Err(ADMIN_PROVIDER_OAUTH_DATA_UNAVAILABLE_DETAIL.to_string());
|
||||
};
|
||||
|
||||
if let Some(refresh_token) = refresh_token {
|
||||
let Some(template) = template else {
|
||||
if provider_type_supports_access_token_import(provider_type) {
|
||||
@@ -301,7 +298,10 @@ pub(super) async fn execute_admin_provider_oauth_batch_import(
|
||||
};
|
||||
|
||||
let template = admin_provider_oauth_template(provider_type);
|
||||
if template.is_none() && !provider_type.eq_ignore_ascii_case("windsurf") {
|
||||
if template.is_none()
|
||||
&& !provider_type.eq_ignore_ascii_case("windsurf")
|
||||
&& !provider_type_supports_access_token_import(provider_type)
|
||||
{
|
||||
return Ok(AdminProviderOAuthBatchImportOutcome {
|
||||
total: entries.len(),
|
||||
success: 0,
|
||||
|
||||
@@ -8,7 +8,7 @@ use super::parse::{
|
||||
};
|
||||
use crate::handlers::admin::provider::oauth::errors::build_internal_control_error_response;
|
||||
use crate::handlers::admin::provider::oauth::state::{
|
||||
build_admin_provider_oauth_backend_unavailable_response,
|
||||
admin_provider_oauth_template, build_admin_provider_oauth_backend_unavailable_response,
|
||||
is_fixed_provider_type_for_provider_oauth,
|
||||
};
|
||||
use crate::handlers::admin::provider::shared::paths::admin_provider_oauth_batch_import_provider_id;
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::progress::{
|
||||
};
|
||||
use crate::handlers::admin::provider::oauth::errors::build_internal_control_error_response;
|
||||
use crate::handlers::admin::provider::oauth::state::{
|
||||
build_admin_provider_oauth_backend_unavailable_response,
|
||||
admin_provider_oauth_template, build_admin_provider_oauth_backend_unavailable_response,
|
||||
is_fixed_provider_type_for_provider_oauth,
|
||||
};
|
||||
use crate::handlers::admin::provider::shared::paths::admin_provider_oauth_batch_import_task_provider_id;
|
||||
|
||||
@@ -508,9 +508,6 @@ pub(super) async fn handle_admin_provider_oauth_import_refresh_token(
|
||||
"Refresh Token 或 Access Token 不能为空",
|
||||
));
|
||||
}
|
||||
let Some(template) = admin_provider_oauth_template(&provider_type) else {
|
||||
return Ok(build_admin_provider_oauth_backend_unavailable_response());
|
||||
};
|
||||
match resolve_admin_provider_oauth_single_import_tokens(
|
||||
state,
|
||||
template,
|
||||
|
||||
@@ -2880,6 +2880,7 @@ async fn gateway_completes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
||||
.expect("account_state_recheck_error should be string when recheck is attempted");
|
||||
assert!(
|
||||
account_state_recheck_error == "wham/usage API 返回状态码 401"
|
||||
|| account_state_recheck_error == "wham/usage API 返回状态码 403"
|
||||
|| account_state_recheck_error.starts_with("wham/usage 请求执行失败:"),
|
||||
"unexpected account_state_recheck_error: {account_state_recheck_error}"
|
||||
);
|
||||
@@ -5351,6 +5352,7 @@ async fn gateway_refreshes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
||||
.expect("account_state_recheck_error should be string when attempted");
|
||||
assert!(
|
||||
account_state_recheck_error == "wham/usage API 返回状态码 401"
|
||||
|| account_state_recheck_error == "wham/usage API 返回状态码 403"
|
||||
|| account_state_recheck_error.starts_with("wham/usage 请求执行失败:"),
|
||||
"unexpected account_state_recheck_error: {account_state_recheck_error}"
|
||||
);
|
||||
@@ -5400,6 +5402,14 @@ async fn gateway_refreshes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
||||
stored_key.oauth_invalid_reason.as_deref(),
|
||||
Some("[OAUTH_EXPIRED] Codex Token 无效或已过期 (401)")
|
||||
);
|
||||
} else if account_state_recheck_attempted
|
||||
&& payload["account_state_recheck_error"] == "wham/usage API 返回状态码 403"
|
||||
{
|
||||
assert!(stored_key.oauth_invalid_at_unix_secs.is_some());
|
||||
assert!(stored_key
|
||||
.oauth_invalid_reason
|
||||
.as_deref()
|
||||
.is_some_and(|reason| reason.contains("(403)")));
|
||||
} else {
|
||||
assert_eq!(stored_key.oauth_invalid_at_unix_secs, None);
|
||||
assert_eq!(stored_key.oauth_invalid_reason, None);
|
||||
|
||||
@@ -1705,6 +1705,7 @@ mod tests {
|
||||
"modelUid": "claude-sonnet-4-6"
|
||||
}
|
||||
}),
|
||||
status_code: 200,
|
||||
};
|
||||
let outcome = fetch_models_from_transports(&runtime, &[sample_windsurf_transport()])
|
||||
.await
|
||||
|
||||
@@ -315,7 +315,7 @@ mod tests {
|
||||
let model = resolve_windsurf_model("gpt-5-5-low").expect("model should resolve");
|
||||
|
||||
assert_eq!(model.canonical_name, "gpt-5.5-low");
|
||||
assert_eq!(model.model_uid.as_deref(), Some("gpt-5-5-low"));
|
||||
assert_eq!(model.model_uid, Some("gpt-5-5-low"));
|
||||
assert_eq!(model.enum_value, 0);
|
||||
assert_eq!(model.credit_multiplier, 1.0);
|
||||
}
|
||||
@@ -325,7 +325,7 @@ mod tests {
|
||||
let model = resolve_windsurf_model("claude-opus-4.7").expect("model should resolve");
|
||||
|
||||
assert_eq!(model.canonical_name, "claude-opus-4-7-medium");
|
||||
assert_eq!(model.model_uid.as_deref(), Some("claude-opus-4-7-medium"));
|
||||
assert_eq!(model.model_uid, Some("claude-opus-4-7-medium"));
|
||||
assert_eq!(model.enum_value, 0);
|
||||
assert_eq!(model.credit_multiplier, 8.0);
|
||||
}
|
||||
@@ -335,7 +335,7 @@ mod tests {
|
||||
let model = resolve_windsurf_model("gpt-5-5-low-priority").expect("model should resolve");
|
||||
|
||||
assert_eq!(model.canonical_name, "gpt-5.5-low-fast");
|
||||
assert_eq!(model.model_uid.as_deref(), Some("gpt-5-5-low-priority"));
|
||||
assert_eq!(model.model_uid, Some("gpt-5-5-low-priority"));
|
||||
assert_eq!(model.credit_multiplier, 2.0);
|
||||
}
|
||||
|
||||
@@ -343,21 +343,18 @@ mod tests {
|
||||
fn resolves_full_gpt55_effort_ladder_and_priority_aliases() {
|
||||
let none = resolve_windsurf_model("gpt-5-5-none").expect("none should resolve");
|
||||
assert_eq!(none.canonical_name, "gpt-5.5-none");
|
||||
assert_eq!(none.model_uid.as_deref(), Some("gpt-5-5-none"));
|
||||
assert_eq!(none.model_uid, Some("gpt-5-5-none"));
|
||||
assert_eq!(none.credit_multiplier, 1.0);
|
||||
|
||||
let high = resolve_windsurf_model("gpt-5.5-high").expect("high should resolve");
|
||||
assert_eq!(high.canonical_name, "gpt-5.5-high");
|
||||
assert_eq!(high.model_uid.as_deref(), Some("gpt-5-5-high"));
|
||||
assert_eq!(high.model_uid, Some("gpt-5-5-high"));
|
||||
assert_eq!(high.credit_multiplier, 4.0);
|
||||
|
||||
let xhigh_fast = resolve_windsurf_model("gpt-5-5-xhigh-priority")
|
||||
.expect("xhigh priority should resolve");
|
||||
assert_eq!(xhigh_fast.canonical_name, "gpt-5.5-xhigh-fast");
|
||||
assert_eq!(
|
||||
xhigh_fast.model_uid.as_deref(),
|
||||
Some("gpt-5-5-xhigh-priority")
|
||||
);
|
||||
assert_eq!(xhigh_fast.model_uid, Some("gpt-5-5-xhigh-priority"));
|
||||
assert_eq!(xhigh_fast.credit_multiplier, 16.0);
|
||||
}
|
||||
|
||||
@@ -365,14 +362,11 @@ mod tests {
|
||||
fn resolves_windsurfapi_catalog_aliases_beyond_gpt55() {
|
||||
let gpt52_medium = resolve_windsurf_model("gpt-5.2-medium").expect("gpt-5.2 medium alias");
|
||||
assert_eq!(gpt52_medium.canonical_name, "gpt-5.2");
|
||||
assert_eq!(
|
||||
gpt52_medium.model_uid.as_deref(),
|
||||
Some("MODEL_GPT_5_2_MEDIUM")
|
||||
);
|
||||
assert_eq!(gpt52_medium.model_uid, Some("MODEL_GPT_5_2_MEDIUM"));
|
||||
|
||||
let haiku = resolve_windsurf_model("claude-haiku-4-5-20251001").expect("dated haiku alias");
|
||||
assert_eq!(haiku.canonical_name, "claude-4.5-haiku");
|
||||
assert_eq!(haiku.model_uid.as_deref(), Some("MODEL_PRIVATE_11"));
|
||||
assert_eq!(haiku.model_uid, Some("MODEL_PRIVATE_11"));
|
||||
|
||||
let uid = resolve_windsurf_model("MODEL_GPT_5_2_LOW").expect("model uid alias");
|
||||
assert_eq!(uid.canonical_name, "gpt-5.2-low");
|
||||
|
||||
Reference in New Issue
Block a user