feat: 候选排序引入 API 格式偏好, 追踪页面展示完整格式转换信息

- 候选排序优先同 kind (chat/cli) 再同 family, 替代原有固定顺序
- 不再隐藏 format_conversion_disabled 候选, 保留完整追踪链路
- DecisionTrace 新增 provider/endpoint/key 格式转换相关字段
- 前端追踪面板新增 Key 支持端点和转换策略展示
This commit is contained in:
fawney19
2026-04-16 13:00:44 +08:00
parent d805a28c9a
commit 9a41b2c0dc
14 changed files with 433 additions and 92 deletions

View File

@@ -76,9 +76,9 @@ async fn admin_monitoring_trace_request_returns_local_payload() {
}
#[tokio::test]
async fn admin_monitoring_trace_request_hides_format_conversion_disabled_candidates() {
let mut hidden_candidate = sample_candidate(
"cand-hidden",
async fn admin_monitoring_trace_request_keeps_format_conversion_disabled_candidates_visible() {
let mut format_disabled_candidate = sample_candidate(
"cand-format-disabled",
"request-1",
0,
RequestCandidateStatus::Skipped,
@@ -86,7 +86,7 @@ async fn admin_monitoring_trace_request_hides_format_conversion_disabled_candida
None,
None,
);
hidden_candidate.skip_reason = Some("format_conversion_disabled".to_string());
format_disabled_candidate.skip_reason = Some("format_conversion_disabled".to_string());
let mut visible_skipped_candidate = sample_candidate(
"cand-visible-skipped",
@@ -100,7 +100,7 @@ async fn admin_monitoring_trace_request_hides_format_conversion_disabled_candida
visible_skipped_candidate.skip_reason = Some("transport_unsupported".to_string());
let request_candidates = Arc::new(InMemoryRequestCandidateRepository::seed(vec![
hidden_candidate,
format_disabled_candidate,
visible_skipped_candidate,
sample_candidate(
"cand-used",
@@ -136,7 +136,7 @@ async fn admin_monitoring_trace_request_hides_format_conversion_disabled_candida
.expect("body should read");
let payload: serde_json::Value = serde_json::from_slice(&body).expect("json body should parse");
assert_eq!(payload["total_candidates"], json!(2));
assert_eq!(payload["total_candidates"], json!(3));
assert_eq!(
payload["candidates"]
.as_array()
@@ -144,10 +144,14 @@ async fn admin_monitoring_trace_request_hides_format_conversion_disabled_candida
.iter()
.map(|item| item["id"].as_str().unwrap_or_default())
.collect::<Vec<_>>(),
vec!["cand-visible-skipped", "cand-used"]
vec!["cand-format-disabled", "cand-visible-skipped", "cand-used"]
);
assert_eq!(
payload["candidates"][0]["skip_reason"],
json!("format_conversion_disabled")
);
assert_eq!(
payload["candidates"][1]["skip_reason"],
json!("transport_unsupported")
);
}

View File

@@ -15,22 +15,6 @@ use axum::{
};
use tracing::debug;
const HIDDEN_TRACE_SKIP_REASONS: &[&str] = &["format_conversion_disabled"];
fn filter_admin_monitoring_trace_candidates(mut trace: DecisionTrace) -> DecisionTrace {
trace.candidates.retain(|item| {
let skip_reason = item
.candidate
.skip_reason
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
!skip_reason.is_some_and(|reason| HIDDEN_TRACE_SKIP_REASONS.contains(&reason))
});
trace.total_candidates = trace.candidates.len();
trace
}
pub(super) async fn build_admin_monitoring_trace_request_response(
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
@@ -67,7 +51,6 @@ pub(super) async fn build_admin_monitoring_trace_request_response(
attempted_only,
));
};
let trace = filter_admin_monitoring_trace_candidates(trace);
Ok(build_admin_monitoring_trace_request_payload_response(
&trace,