新增: F-04多Provider负载均衡池(数据层+选择器+并发原语)+CR-52白项
This commit is contained in:
@@ -130,6 +130,32 @@ pub(crate) async fn run_agentic_loop(
|
||||
// B-260615-09: generating 状态由 RAII guard 收敛复位(正常 exit 显式 reset;panic/异常 Drop 兜底)
|
||||
let mut guard = GeneratingGuard::new(session_arc.clone());
|
||||
|
||||
// F-260614-04: 多 Provider 负载均衡池 — 选主候选 provider。
|
||||
//
|
||||
// 流程:list_all → ProviderPool::select(按 模型亲和 > weight > is_default 排序)→ 取首位。
|
||||
// 单 provider 场景:池仅 1 enabled provider → select 返回单元素 Vec → 首位 = 唯一 provider,
|
||||
// 行为同 F-01 前(零变化)。空池(0 enabled)→ fallback 入参 provider_config(保启动行为)。
|
||||
//
|
||||
// 主候选的 model_configs 用于路由(F-01),其 provider_config 用于 build_provider。
|
||||
// 当前批次仅选主,fallback(主失败切备用 provider)见后续批次(需重构流式重试块,单独评估)。
|
||||
//
|
||||
// AiProviderRepo::new 仅 clone Arc<Database>(廉价),不复用 AppState.ai_providers
|
||||
// (run_agentic_loop 签名只传 Arc<Database>,改签名会牵动 3 调用点 + try_continue)。
|
||||
let provider_repo = df_storage::crud::AiProviderRepo::new(&db);
|
||||
let pool_providers: Vec<AiProviderRecord> = provider_repo.list_all().await.unwrap_or_default();
|
||||
let primary_provider: AiProviderRecord = match super::provider_pool::ProviderPool::select(
|
||||
&pool_providers,
|
||||
None, // 模型亲和此时尚未确定(router 选模型需 provider_config,见下方)
|
||||
)
|
||||
.into_iter()
|
||||
.next()
|
||||
{
|
||||
Some(p) => p,
|
||||
None => provider_config.clone(), // 空池兜底:用调用方传入的 provider_config(启动行为不变)
|
||||
};
|
||||
// 用主候选覆盖入参 provider_config(下游 build_provider / 路由 / 日志均用此)。
|
||||
let provider_config = primary_provider;
|
||||
|
||||
// FR-S1: resolve→ensure_resolved_key(空 key 早失败)→build_provider 三步统一走工厂
|
||||
// 空 key 早失败(逻辑见 secret::ensure_resolved_key 单测):避免空 key 发请求吃 401,错误伪装成"API Key 无效"
|
||||
//
|
||||
@@ -400,8 +426,13 @@ pub(crate) async fn run_agentic_loop(
|
||||
// Partial)不重试保文。重试退避复用 retry::backoff_delay(1s→2s→4s±20% jitter) +
|
||||
// retry::is_status_retryable Fatal 分类(stream_recv classify_status_or_class 镜像,
|
||||
// 4xx 非429 立即放弃) + 30s 总挂钟预算。重试期间持有 permit 不释放(防新请求挤占)。
|
||||
//
|
||||
// F-260614-04: per-provider permit(可选)。set_provider_caps 未配置时返回 None
|
||||
// (单 provider 场景零变化);配置后取额外 permit 防单 provider 被打满(限流 429)。
|
||||
// 三 permit 均 Drop 释放(L595-597 显式 drop _global/_per_conv;_provider_permit 绑块尾 Drop)。
|
||||
let _global_permit = llm_concurrency.acquire_global().await;
|
||||
let _per_conv_permit = llm_concurrency.acquire_per_conv().await;
|
||||
let _provider_permit = llm_concurrency.acquire_for_provider(&provider_config.id).await;
|
||||
|
||||
// 重试总预算(挂钟,含 sleep + 各次请求耗时),对齐 retry::MAX_TOTAL_BUDGET 30s。
|
||||
// 超预算直接放弃重试交最终错误/保文路径。
|
||||
@@ -569,6 +600,8 @@ pub(crate) async fn run_agentic_loop(
|
||||
// stream 结束立即释放 permit,后续工具执行不受限流(本地操作无 RPM 成本)
|
||||
drop(_global_permit);
|
||||
drop(_per_conv_permit);
|
||||
// F-260614-04: per-provider permit 为 None 时 drop None 无副作用;Some 时释放槽。
|
||||
drop(_provider_permit);
|
||||
|
||||
// 累加本轮 token:provider 流式 usage 的 prompt_tokens 为 0 时(GLM 等),用预估输入兜底
|
||||
let round_prompt = if round_usage.prompt_tokens == 0 { estimated_prompt } else { round_usage.prompt_tokens };
|
||||
|
||||
Reference in New Issue
Block a user