新增: F-04多Provider负载均衡池(数据层+选择器+并发原语)+CR-52白项
This commit is contained in:
@@ -78,10 +78,10 @@ impl Default for KnowledgeConfig {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// LLM 调用并发控制(双层 Semaphore)
|
||||
// LLM 调用并发控制(双层 Semaphore + 可选 per-provider 层)
|
||||
// ============================================================
|
||||
|
||||
/// LLM 调用并发控制 — 全局 + 单对话双层 Semaphore
|
||||
/// LLM 调用并发控制 — 全局 + 单对话双层 Semaphore + 可选 per-provider 层
|
||||
///
|
||||
/// 限流对象:所有真实 LLM 调用(主循环 stream_llm / 标题生成 / 知识提炼)。
|
||||
/// 不限流本地工具执行(tools.execute)——本地操作无外部成本、不受 RPM 约束。
|
||||
@@ -95,10 +95,21 @@ impl Default for KnowledgeConfig {
|
||||
/// generating 互斥,同一时刻仅一个对话的 loop 在跑,per_conv 退化为"单对话内并发"
|
||||
/// (主循环 stream_llm + 标题生成 + 知识提炼)。未来若支持多对话并发,
|
||||
/// 需改为 HashMap<conv_id, Semaphore>。
|
||||
///
|
||||
/// ## F-260614-04: per-provider 层(可选)
|
||||
/// `per_provider` 为 HashMap<provider_id, Arc<Semaphore>>。调用方(agentic loop)经
|
||||
/// `acquire_for_provider(pid)` 取额外 permit,防单 provider 被打满(限流 429)。
|
||||
/// **单 provider 场景**:若未调 `set_provider_caps`,HashMap 为空,
|
||||
/// `acquire_for_provider` 返回 None(无限流,行为同 F-01 前)。零变化保证。
|
||||
/// 全局容量 = min(sum(各 provider 上限), global_cap):由调用方在配置时约束
|
||||
/// (set_provider_caps 传 min(sum, global_cap)),非运行时强约束。
|
||||
#[derive(Clone)]
|
||||
pub struct LlmConcurrency {
|
||||
global: Arc<Mutex<Arc<Semaphore>>>,
|
||||
per_conv: Arc<Mutex<Arc<Semaphore>>>,
|
||||
/// F-260614-04: per-provider 信号量表。空 = 无 per-provider 限流(单 provider 路径零变化)。
|
||||
/// Arc<Mutex<HashMap>>:运行时增删 provider 配置时替换/插入,acquire 时 clone Arc。
|
||||
per_provider: Arc<Mutex<HashMap<String, Arc<Semaphore>>>>,
|
||||
}
|
||||
|
||||
impl LlmConcurrency {
|
||||
@@ -106,6 +117,7 @@ impl LlmConcurrency {
|
||||
Self {
|
||||
global: Arc::new(Mutex::new(Arc::new(Semaphore::new(global)))),
|
||||
per_conv: Arc::new(Mutex::new(Arc::new(Semaphore::new(per_conv)))),
|
||||
per_provider: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +142,54 @@ impl LlmConcurrency {
|
||||
pub async fn set_per_conv(&self, permits: usize) {
|
||||
*self.per_conv.lock().await = Arc::new(Semaphore::new(permits));
|
||||
}
|
||||
|
||||
/// F-260614-04: 取 per-provider 并发 permit(可选)。
|
||||
///
|
||||
/// - provider 在 `per_provider` 表中有配置 → 取其 Semaphore permit,返回 Some。
|
||||
/// - provider 无配置(单 provider 场景或未 set_provider_caps)→ 返回 None,无限流。
|
||||
///
|
||||
/// 调用方(agentic loop)用法:
|
||||
/// ```ignore
|
||||
/// 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_id).await;
|
||||
/// ```
|
||||
/// 三 permit 均绑 guard Drop 自动释放。None 时无 permit 需释放(行为同 F-01 前)。
|
||||
pub async fn acquire_for_provider(
|
||||
&self,
|
||||
provider_id: &str,
|
||||
) -> Option<tokio::sync::OwnedSemaphorePermit> {
|
||||
let sema = {
|
||||
let map = self.per_provider.lock().await;
|
||||
map.get(provider_id).cloned()
|
||||
}?;
|
||||
// Semaphore 存在 → acquire。expect 同 acquire_global/per_conv:Semaphore 不会 close
|
||||
// (无 close() 调用,仅在 set_provider_caps 时替换表内 Arc,旧 Arc permit 仍有效)。
|
||||
Some(
|
||||
sema.acquire_owned()
|
||||
.await
|
||||
.expect("llm per_provider semaphore closed"),
|
||||
)
|
||||
}
|
||||
|
||||
/// F-260614-04: 批量设置 per-provider 并发上限(替换整表)。
|
||||
///
|
||||
/// 调用方(Settings 配置热改 / 启动初始化)传入 `{ provider_id: permits }` map,
|
||||
/// 替换整张 per_provider 表(软收敛:已持 permit 不回收)。全局容量约束 min(sum, global_cap)
|
||||
/// 由调用方在构造 map 时应用(本函数不做强约束,只落表)。
|
||||
///
|
||||
/// 传空 map → 清空 per_provider 表(所有 provider 回退到无 per-provider 限流)。
|
||||
pub async fn set_provider_caps(&self, caps: HashMap<String, usize>) {
|
||||
let mut map = self.per_provider.lock().await;
|
||||
map.clear();
|
||||
for (pid, permits) in caps {
|
||||
// permits=0 等同无配置(Semaphore::new(0) 永远 acquire 不到 → 死锁),
|
||||
// 故 permits=0 跳过(不落表 → acquire_for_provider 返 None → 无限流)。
|
||||
if permits > 0 {
|
||||
map.insert(pid, Arc::new(Semaphore::new(permits)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 应用全局状态 — 通过 `app.manage()` 注入,command 中以 `State<'_, AppState>` 取用
|
||||
|
||||
Reference in New Issue
Block a user