修复: 模型停用无效根因 + 上下文归位/压缩守卫
- 模型停用无效根因: get_active_provider 两条路径均不校验 enabled(显式活跃直返/默认只挑 is_default) → 收敛为启用集合解析,无启用 provider 明确报错 - 压缩/标题兜底模型不校验 enabled → 新增 resolve_fallback_model 纯函数(title 复用同一实现,单一源无双轨) - 上下文多夹层归位修复: gather_tool_responses 聚拢该头全部响应(此前第二个夹层之后的响应仍被判未闭合剥除) - 压缩后台任务加内容版本哨兵(取首条消息 id,否掉 len:压缩期间 loop 正常 append 会致守卫永不生效) + panic 兜底复位标志 - 902 超时类失败与其它 Fatal 区分停靠文案(点继续可续跑) - 子代理事件按 conv 改挂(route_scope) + 运行期事件推父会话视图 - 飞书 13a 真根因: run_stream_refresher 死代码(提前 drop)+静默钟被心跳重置 → 帧通道+仅内容事件重置 - 项目详情文件查看: F22 目录不存在误显非 Git 仓库 / F24 模块为仓库子目录时 git 徽标全失效(scope_git_path 按 show-prefix 剥前缀) - 更新徽章通道错配(Tauri 事件 vs window CustomEvent 同名不同通道,徽章此前永不触发) - ProjectCard 卡片根 keydown 顶替内嵌按钮(补 e.target 守卫) - i18n 坏键 ai.aiTool.rejectedHint → aiTool.rejectedHint(双缺会渲染裸 key) - miniapp 三处: 会话列表刷新漏网/看门狗节流致新一轮无兜底/newConversation 漏清看门狗
This commit is contained in:
@@ -9,30 +9,46 @@ use df_storage::models::AiProviderRecord;
|
||||
use crate::commands::err_str;
|
||||
use crate::state::{AppState, SettingsNorm};
|
||||
|
||||
/// 获取当前活跃提供商配置
|
||||
/// 获取当前活跃提供商配置。
|
||||
///
|
||||
/// **停用须生效(2026-10-10 用户报「模型配置停用无效」根因)**:原实现显式活跃 provider 直接返回、
|
||||
/// 默认 provider 只按 `is_default` 挑,**全程不看 `enabled`** → 用户在设置里停用某 provider 后
|
||||
/// 会话仍照用(停用形同虚设)。现两条路径都收敛到「启用集合」:显式活跃项被停用 → 改按启用集合解析
|
||||
/// (留 warn 便于定位);无任何启用 provider → 明确报错而非静默使用被停用项。
|
||||
pub(crate) async fn get_active_provider(state: &AppState) -> Result<AiProviderRecord, String> {
|
||||
let session = state.ai_session.lock().await;
|
||||
if let Some(ref pid) = session.active_provider_id {
|
||||
let provider = state
|
||||
.ai_providers
|
||||
.get_by_id(pid)
|
||||
.await
|
||||
.map_err(err_str)?
|
||||
.ok_or_else(|| format!("活跃提供商不存在: {}", pid))?;
|
||||
Ok(provider)
|
||||
} else {
|
||||
// 查找默认提供商
|
||||
drop(session);
|
||||
let providers = state
|
||||
.ai_providers
|
||||
.list_all()
|
||||
.await
|
||||
.map_err(err_str)?;
|
||||
let default = providers.iter().find(|p| p.is_default).cloned();
|
||||
default
|
||||
.or_else(|| providers.into_iter().next())
|
||||
.ok_or_else(|| "未配置 AI 提供商,请先在设置中添加".to_string())
|
||||
if let Some(provider) = state.ai_providers.get_by_id(pid).await.map_err(err_str)? {
|
||||
if provider.enabled {
|
||||
return Ok(provider);
|
||||
}
|
||||
tracing::warn!(
|
||||
provider_id = %pid,
|
||||
provider_name = %provider.name,
|
||||
"[ai] 活跃 provider 已被停用,改按启用集合解析默认 provider"
|
||||
);
|
||||
} else {
|
||||
tracing::warn!(provider_id = %pid, "[ai] 活跃 provider 不存在,改按启用集合解析");
|
||||
}
|
||||
}
|
||||
drop(session);
|
||||
resolve_enabled_provider(state).await
|
||||
}
|
||||
|
||||
/// 在**启用**的 provider 中解析:`is_default` 优先,否则取列表首个(与旧行为同序,仅多一道 enabled 过滤)。
|
||||
async fn resolve_enabled_provider(state: &AppState) -> Result<AiProviderRecord, String> {
|
||||
let providers = state
|
||||
.ai_providers
|
||||
.list_all()
|
||||
.await
|
||||
.map_err(err_str)?;
|
||||
let enabled: Vec<AiProviderRecord> = providers.into_iter().filter(|p| p.enabled).collect();
|
||||
enabled
|
||||
.iter()
|
||||
.find(|p| p.is_default)
|
||||
.cloned()
|
||||
.or_else(|| enabled.into_iter().next())
|
||||
.ok_or_else(|| "未启用任何 AI 提供商,请在设置中启用一个(或新增)".to_string())
|
||||
}
|
||||
|
||||
/// 当前运行环境画像(注入 system prompt):日期 + OS + shell 执行姿势 + 可用解释器。
|
||||
@@ -45,7 +61,10 @@ pub(crate) async fn get_active_provider(state: &AppState) -> Result<AiProviderRe
|
||||
/// - 可用解释器:Windows=python(无 python3);Linux·macOS=python3;node 均有
|
||||
///
|
||||
/// 跨设备:cfg! 分支,平台各自正确姿势。补充探测见 detect_environment 工具(失败自愈)。
|
||||
fn env_profile_line() -> String {
|
||||
///
|
||||
/// pub(crate):子代理 system prompt 同源注入(见 sub_agent::sub_agent_system_prompt),
|
||||
/// 子代理同样需要日期(不能自行获取)与 shell 执行姿势。
|
||||
pub(crate) fn env_profile_line() -> String {
|
||||
let today = chrono::Local::now().format("%Y-%m-%d");
|
||||
format!("当前日期: {today} | {}", env_profile_stable_body())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user