mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(pool): 引入 pro_first 调度预设、Pool 候选持久化跳过与诊断信息优化
- 新增 pro_first 调度预设(Pro 优先),更新 plus_first 仅针对 Plus 计划,移除 free_team_first - Pool 内部候选(pool_key_index 不为空)跳过 DB 持久化(available/skipped/unused 均适用) - LRU 排序新增 catalog_lru_score 回退:runtime 无记录时使用 last_used_at_unix_secs - 执行路径 miss 诊断消息细化为中文,按 reason 分类输出可读说明 - build_local_request_candidate_status_record 补充 extra_data 和 created_at_unix_ms 字段 - OpenAI CLI 计划构建流程补充候选评估进度跟踪与 terminal reason 设置 - 前端 PoolSchedulingDialog 增加 pro_first 预设展示,修复 LRU 默认预设检测逻辑
This commit is contained in:
@@ -120,7 +120,7 @@ async fn gateway_locally_denies_sync_ai_control_execute_when_opted_in_and_execut
|
||||
);
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -255,7 +255,7 @@ async fn gateway_locally_denies_stream_ai_control_execute_when_opted_in_and_exec
|
||||
);
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -393,7 +393,7 @@ async fn gateway_does_not_proxy_control_execute_over_http_when_opted_in_and_exec
|
||||
let payload: serde_json::Value = response.json().await.expect("body should parse");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
assert_eq!(*plan_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -526,7 +526,7 @@ async fn gateway_does_not_proxy_control_execute_over_http_when_opted_in_and_exec
|
||||
let payload: serde_json::Value = response.json().await.expect("body should parse");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
assert_eq!(*plan_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
@@ -122,7 +122,7 @@ async fn gateway_locally_denies_openai_chat_after_repeated_execution_runtime_mis
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ async fn gateway_locally_denies_openai_chat_when_control_api_is_configured_witho
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -359,7 +359,7 @@ async fn gateway_locally_denies_openai_chat_stream_after_execution_runtime_miss_
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI chat execution runtime miss did not match a Rust execution path"
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Chat Completions,原因代码: missing_auth_context)"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -537,7 +537,7 @@ async fn gateway_locally_denies_openai_responses_after_execution_runtime_miss_wi
|
||||
"cli",
|
||||
"openai:cli",
|
||||
"{\"model\":\"gpt-5\",\"input\":\"hello\"}",
|
||||
"OpenAI responses execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Responses,原因代码: missing_auth_context)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -551,7 +551,7 @@ async fn gateway_locally_denies_claude_messages_after_execution_runtime_miss_wit
|
||||
"chat",
|
||||
"claude:chat",
|
||||
"{\"model\":\"claude-sonnet-4-5\",\"messages\":[]}",
|
||||
"Claude messages execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少本地执行所需的认证、模型或配置上下文,无法选择上游提供商(Claude Messages,原因代码: decision_input_unavailable)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -565,7 +565,7 @@ async fn gateway_locally_denies_openai_responses_stream_after_execution_runtime_
|
||||
"cli",
|
||||
"openai:cli",
|
||||
"{\"model\":\"gpt-5\",\"input\":\"hello\",\"stream\":true}",
|
||||
"OpenAI responses execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Responses,原因代码: missing_auth_context)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -579,7 +579,7 @@ async fn gateway_locally_denies_claude_messages_stream_after_execution_runtime_m
|
||||
"chat",
|
||||
"claude:chat",
|
||||
"{\"model\":\"claude-sonnet-4-5\",\"messages\":[],\"stream\":true}",
|
||||
"Claude messages execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少本地执行所需的认证、模型或配置上下文,无法选择上游提供商(Claude Messages,原因代码: decision_input_unavailable)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -593,7 +593,7 @@ async fn gateway_locally_denies_openai_compact_after_execution_runtime_miss_with
|
||||
"compact",
|
||||
"openai:compact",
|
||||
"{\"model\":\"gpt-5\",\"input\":\"hello\"}",
|
||||
"OpenAI compact execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Responses Compact,原因代码: missing_auth_context)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -607,7 +607,7 @@ async fn gateway_locally_denies_openai_compact_stream_after_execution_runtime_mi
|
||||
"compact",
|
||||
"openai:compact",
|
||||
"{\"model\":\"gpt-5\",\"input\":\"hello\",\"stream\":true}",
|
||||
"OpenAI compact execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少有效的用户或 API Key 认证上下文,无法选择上游提供商(OpenAI Responses Compact,原因代码: missing_auth_context)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -621,7 +621,7 @@ async fn gateway_locally_denies_gemini_generate_after_execution_runtime_miss_wit
|
||||
"chat",
|
||||
"gemini:chat",
|
||||
"{\"contents\":[]}",
|
||||
"Gemini public execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少本地执行所需的认证、模型或配置上下文,无法选择上游提供商(Gemini Public,原因代码: decision_input_unavailable)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -635,7 +635,7 @@ async fn gateway_locally_denies_gemini_v1_generate_after_execution_runtime_miss_
|
||||
"chat",
|
||||
"gemini:chat",
|
||||
"{\"contents\":[]}",
|
||||
"Gemini public execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少本地执行所需的认证、模型或配置上下文,无法选择上游提供商(Gemini Public,原因代码: decision_input_unavailable)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -649,7 +649,7 @@ async fn gateway_locally_denies_gemini_stream_after_execution_runtime_miss_witho
|
||||
"chat",
|
||||
"gemini:chat",
|
||||
"{\"contents\":[]}",
|
||||
"Gemini public execution runtime miss did not match a Rust execution path",
|
||||
"请求缺少本地执行所需的认证、模型或配置上下文,无法选择上游提供商(Gemini Public,原因代码: decision_input_unavailable)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -663,7 +663,7 @@ async fn gateway_locally_denies_openai_video_after_execution_runtime_miss_withou
|
||||
"video",
|
||||
"openai:video",
|
||||
"{\"model\":\"sora-2\"}",
|
||||
"OpenAI video execution runtime miss did not match a Rust execution path",
|
||||
"当前 OpenAI Video 请求无法在本地执行:没有匹配到可用的执行路径",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -677,7 +677,7 @@ async fn gateway_locally_denies_gemini_video_after_execution_runtime_miss_withou
|
||||
"video",
|
||||
"gemini:video",
|
||||
"{\"instances\":[]}",
|
||||
"Gemini public execution runtime miss did not match a Rust execution path",
|
||||
"当前 Gemini Public 请求无法在本地执行:没有匹配到可用的执行路径",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -693,7 +693,7 @@ async fn gateway_locally_denies_gemini_files_root_after_execution_runtime_miss_w
|
||||
"files",
|
||||
"gemini:chat",
|
||||
None,
|
||||
"Gemini files execution runtime miss did not match a Rust execution path",
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -709,7 +709,7 @@ async fn gateway_locally_denies_gemini_files_download_after_execution_runtime_mi
|
||||
"files",
|
||||
"gemini:chat",
|
||||
None,
|
||||
"Gemini files execution runtime miss did not match a Rust execution path",
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -725,7 +725,7 @@ async fn gateway_locally_denies_gemini_files_upload_after_execution_runtime_miss
|
||||
"files",
|
||||
"gemini:chat",
|
||||
Some("{\"file\":{}}"),
|
||||
"Gemini files execution runtime miss did not match a Rust execution path",
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -654,7 +654,7 @@ async fn gateway_surfaces_local_execution_runtime_miss_reason_when_all_openai_ch
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"没有可用的提供商支持模型 gpt-5 的同步请求"
|
||||
"找到 1 个支持模型 gpt-5 的候选提供商,但本次同步请求全部不可用:提供商类型不支持本地执行 2 次(原因代码: all_candidates_skipped)"
|
||||
);
|
||||
|
||||
let stored_candidates = request_candidate_repository
|
||||
|
||||
@@ -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 的同步请求"
|
||||
"没有可用提供商支持模型 claude-sonnet-4-5 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
);
|
||||
|
||||
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 的同步请求"
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
);
|
||||
|
||||
let stored_candidates = request_candidate_repository
|
||||
|
||||
@@ -187,10 +187,11 @@ async fn gateway_handles_admin_pool_scheduling_presets_locally_with_trusted_admi
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let payload: serde_json::Value = response.json().await.expect("json body should parse");
|
||||
let items = payload.as_array().expect("payload should be an array");
|
||||
assert_eq!(items.len(), 13);
|
||||
assert_eq!(items.len(), 14);
|
||||
assert_eq!(items[0]["name"], "lru");
|
||||
assert_eq!(items[1]["name"], "cache_affinity");
|
||||
assert_eq!(items[12]["name"], "team_first");
|
||||
assert_eq!(items[8]["name"], "pro_first");
|
||||
assert_eq!(items[13]["name"], "team_first");
|
||||
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
gateway_handle.abort();
|
||||
|
||||
@@ -227,7 +227,7 @@ async fn gateway_locally_denies_gemini_files_download_control_sync_even_with_opt
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"Gemini files execution runtime miss did not match a Rust execution path"
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -306,7 +306,7 @@ async fn gateway_locally_denies_gemini_files_download_control_sync_without_opt_i
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"Gemini files execution runtime miss did not match a Rust execution path"
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -378,7 +378,7 @@ async fn gateway_skips_gemini_files_download_control_sync_without_opt_in_header(
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"Gemini files execution runtime miss did not match a Rust execution path"
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
@@ -338,7 +338,7 @@ async fn gateway_locally_denies_gemini_files_upload_control_sync_with_opt_in_hea
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"Gemini files execution runtime miss did not match a Rust execution path"
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -403,7 +403,7 @@ async fn gateway_locally_denies_gemini_files_upload_control_sync_without_opt_in_
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"Gemini files execution runtime miss did not match a Rust execution path"
|
||||
"当前 Gemini Files 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
@@ -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 的同步请求"
|
||||
"没有可用提供商支持模型 claude-sonnet-4-5 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
);
|
||||
|
||||
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 的同步请求"
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
);
|
||||
|
||||
let stored_usage = wait_for_usage_status(
|
||||
@@ -1614,7 +1614,9 @@ async fn gateway_records_failed_usage_when_all_local_claude_cli_candidates_are_s
|
||||
);
|
||||
assert_eq!(
|
||||
stored_usage.error_message.as_deref(),
|
||||
Some("没有可用的提供商支持模型 gpt-5.4 的同步请求")
|
||||
Some(
|
||||
"没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
stored_usage
|
||||
|
||||
@@ -71,7 +71,7 @@ async fn gateway_locally_denies_video_control_sync_even_with_opt_in_headers_when
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI video execution runtime miss did not match a Rust execution path"
|
||||
"当前 OpenAI Video 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -147,7 +147,7 @@ async fn gateway_locally_denies_video_control_sync_without_opt_in_header_when_ex
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI video execution runtime miss did not match a Rust execution path"
|
||||
"当前 OpenAI Video 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
@@ -216,7 +216,7 @@ async fn gateway_skips_video_get_control_sync_without_opt_in_header() {
|
||||
assert_eq!(payload["error"]["type"], "http_error");
|
||||
assert_eq!(
|
||||
payload["error"]["message"],
|
||||
"OpenAI video execution runtime miss did not match a Rust execution path"
|
||||
"当前 OpenAI Video 请求无法在本地执行:没有匹配到可用的执行路径"
|
||||
);
|
||||
assert_eq!(*execute_hits.lock().expect("mutex should lock"), 0);
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
Reference in New Issue
Block a user