mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix(usage): 简化本地执行错误提示
This commit is contained in:
@@ -16,9 +16,10 @@ pub(crate) use candidate_loop::{
|
||||
};
|
||||
pub(crate) use orchestration::*;
|
||||
pub(crate) use outcome::{
|
||||
build_local_execution_exhaustion, build_local_execution_runtime_miss_context,
|
||||
record_failed_usage_for_exhausted_request, record_failed_usage_for_runtime_miss_request,
|
||||
LocalExecutionExhaustion, LocalExecutionRequestOutcome, LocalExecutionRuntimeMissContext,
|
||||
beautify_local_execution_client_error_message, build_local_execution_exhaustion,
|
||||
build_local_execution_runtime_miss_context, record_failed_usage_for_exhausted_request,
|
||||
record_failed_usage_for_runtime_miss_request, LocalExecutionExhaustion,
|
||||
LocalExecutionRequestOutcome, LocalExecutionRuntimeMissContext,
|
||||
};
|
||||
pub(crate) use plan_fallback::{
|
||||
maybe_execute_stream_via_plan_fallback, maybe_execute_sync_via_plan_fallback,
|
||||
|
||||
@@ -252,7 +252,7 @@ pub(crate) async fn record_failed_usage_for_exhausted_request(
|
||||
data.client_response_body = Some(json!({
|
||||
"error": {
|
||||
"type": "http_error",
|
||||
"message": local_execution_runtime_miss_detail,
|
||||
"message": beautify_local_execution_client_error_message(local_execution_runtime_miss_detail),
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -322,10 +322,12 @@ pub(crate) async fn record_failed_usage_for_runtime_miss_request(
|
||||
.filter(|value| !value.eq_ignore_ascii_case(model.as_str()));
|
||||
|
||||
let status_code = http::StatusCode::SERVICE_UNAVAILABLE.as_u16();
|
||||
let client_message =
|
||||
beautify_local_execution_client_error_message(local_execution_runtime_miss_detail);
|
||||
let client_body = json!({
|
||||
"error": {
|
||||
"type": "http_error",
|
||||
"message": local_execution_runtime_miss_detail,
|
||||
"message": client_message,
|
||||
}
|
||||
});
|
||||
let mut client_headers = Map::from_iter([(
|
||||
@@ -418,6 +420,66 @@ pub(crate) async fn record_failed_usage_for_runtime_miss_request(
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(crate) fn beautify_local_execution_client_error_message(message: &str) -> String {
|
||||
let without_reason_code = strip_parenthesized_reason_code(message);
|
||||
let mut simplified = collapse_whitespace(without_reason_code.as_str());
|
||||
for marker in [
|
||||
"。请检查",
|
||||
"。请确认",
|
||||
". 请检查",
|
||||
". 请确认",
|
||||
"! 请检查",
|
||||
"! 请确认",
|
||||
"? 请检查",
|
||||
"? 请确认",
|
||||
"。Reason",
|
||||
". Reason",
|
||||
"。Code",
|
||||
". Code",
|
||||
] {
|
||||
if let Some(index) = simplified.find(marker) {
|
||||
simplified.truncate(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
trim_trailing_message_punctuation(simplified.as_str()).to_string()
|
||||
}
|
||||
|
||||
fn strip_parenthesized_reason_code(message: &str) -> String {
|
||||
let Some(reason_index) = message.find("原因代码") else {
|
||||
return message.to_string();
|
||||
};
|
||||
let Some((start, open)) = message[..reason_index]
|
||||
.char_indices()
|
||||
.rev()
|
||||
.find(|(_, ch)| *ch == '(' || *ch == '(')
|
||||
else {
|
||||
return message.to_string();
|
||||
};
|
||||
let close = if open == '(' { ')' } else { ')' };
|
||||
let Some(close_offset) = message[start..].find(close) else {
|
||||
return message[..start].to_string();
|
||||
};
|
||||
let end = start + close_offset + close.len_utf8();
|
||||
format!("{}{}", &message[..start], &message[end..])
|
||||
}
|
||||
|
||||
fn collapse_whitespace(message: &str) -> String {
|
||||
message.split_whitespace().collect::<Vec<_>>().join(" ")
|
||||
}
|
||||
|
||||
fn trim_trailing_message_punctuation(message: &str) -> &str {
|
||||
message
|
||||
.trim_end_matches(|ch: char| {
|
||||
ch.is_whitespace()
|
||||
|| matches!(
|
||||
ch,
|
||||
'。' | '.' | '!' | '?' | ';' | ';' | ',' | ',' | ':' | ':'
|
||||
)
|
||||
})
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn select_last_failed_request_candidate(
|
||||
candidates: &[StoredRequestCandidate],
|
||||
) -> Option<&StoredRequestCandidate> {
|
||||
@@ -870,7 +932,8 @@ fn trimmed_non_empty(value: Option<&str>) -> Option<String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
apply_runtime_miss_usage_routing, request_candidate_represents_provider_execution,
|
||||
apply_runtime_miss_usage_routing, beautify_local_execution_client_error_message,
|
||||
request_candidate_represents_provider_execution,
|
||||
select_last_runtime_miss_executed_candidate, RuntimeMissCandidateContext,
|
||||
};
|
||||
use crate::constants::EXECUTION_PATH_LOCAL_EXECUTION_RUNTIME_MISS;
|
||||
@@ -881,6 +944,22 @@ mod tests {
|
||||
use aether_usage_runtime::UsageEventData;
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
#[test]
|
||||
fn local_execution_client_error_message_is_client_friendly() {
|
||||
assert_eq!(
|
||||
beautify_local_execution_client_error_message(
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)",
|
||||
),
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求"
|
||||
);
|
||||
assert_eq!(
|
||||
beautify_local_execution_client_error_message(
|
||||
"请求缺少 model 字段,无法选择上游提供商(openai/chat,原因代码: missing_requested_model)",
|
||||
),
|
||||
"请求缺少 model 字段,无法选择上游提供商"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_miss_routing_moves_to_typed_usage_fields_and_keeps_metadata_lightweight() {
|
||||
let mut data = UsageEventData::default();
|
||||
|
||||
@@ -36,9 +36,10 @@ use crate::control::{
|
||||
GatewayPublicRequestContext,
|
||||
};
|
||||
use crate::executor::{
|
||||
build_local_execution_runtime_miss_context, maybe_execute_stream_request,
|
||||
maybe_execute_sync_request, record_failed_usage_for_exhausted_request,
|
||||
record_failed_usage_for_runtime_miss_request, LocalExecutionRequestOutcome,
|
||||
beautify_local_execution_client_error_message, build_local_execution_runtime_miss_context,
|
||||
maybe_execute_stream_request, maybe_execute_sync_request,
|
||||
record_failed_usage_for_exhausted_request, record_failed_usage_for_runtime_miss_request,
|
||||
LocalExecutionRequestOutcome,
|
||||
};
|
||||
use crate::frontdoor_loop_guard::{
|
||||
frontdoor_self_loop_public_ai_path, request_has_execution_runtime_loop_guard,
|
||||
@@ -1364,7 +1365,10 @@ pub(crate) async fn proxy_request(
|
||||
&trace_id,
|
||||
control_decision,
|
||||
http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
local_execution_runtime_miss_detail.as_str(),
|
||||
beautify_local_execution_client_error_message(
|
||||
local_execution_runtime_miss_detail.as_str(),
|
||||
)
|
||||
.as_str(),
|
||||
)?;
|
||||
let local_execution_runtime_miss_reason = local_execution_runtime_miss_diagnostic
|
||||
.as_ref()
|
||||
|
||||
@@ -562,7 +562,7 @@ async fn gateway_surfaces_candidate_list_empty_reason_for_claude_chat_runtime_mi
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"没有可用提供商支持模型 claude-sonnet-4-5 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
"没有可用提供商支持模型 claude-sonnet-4-5 的同步请求"
|
||||
);
|
||||
|
||||
let stored_candidates = request_candidate_repository
|
||||
|
||||
@@ -958,7 +958,7 @@ async fn gateway_marks_claude_cli_cross_format_runtime_miss_when_format_conversi
|
||||
assert_eq!(response_json["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
response_json["error"]["message"],
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求"
|
||||
);
|
||||
|
||||
let stored_candidates = request_candidate_repository
|
||||
|
||||
@@ -955,7 +955,7 @@ async fn gateway_records_failed_usage_for_claude_runtime_miss_without_execution_
|
||||
assert_eq!(body_json["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
body_json["error"]["message"],
|
||||
"没有可用提供商支持模型 claude-sonnet-4-5 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
"没有可用提供商支持模型 claude-sonnet-4-5 的同步请求"
|
||||
);
|
||||
|
||||
let stored_usage = wait_for_usage_status(
|
||||
@@ -1576,7 +1576,7 @@ async fn gateway_records_failed_usage_when_all_local_claude_cli_candidates_are_s
|
||||
assert_eq!(body_json["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
body_json["error"]["message"],
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求"
|
||||
);
|
||||
|
||||
let stored_usage = wait_for_usage_status(
|
||||
|
||||
Reference in New Issue
Block a user