新增: F-01模型拉取IPC+Settings拉取UI(FR-S1密钥内存解析闭环)
This commit is contained in:
@@ -6,6 +6,8 @@ use serde::Serialize;
|
||||
use tauri::{AppHandle, Emitter, State};
|
||||
|
||||
use df_ai::provider::ChatMessage;
|
||||
// df-ai 重导出 df_ai_core(供下游直接引用 trait/类型);src-tauri 不直接依赖 df-ai-core crate。
|
||||
use df_ai::df_ai_core::model::ModelConfig;
|
||||
use df_core::types::new_id;
|
||||
use df_storage::models::AiProviderRecord;
|
||||
|
||||
@@ -827,6 +829,78 @@ pub async fn ai_delete_provider(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 模型列表拉取 + 单模型探测(F-01 阶段5 IPC)
|
||||
// ============================================================
|
||||
|
||||
/// 将前端 provider_type 规范化为 fetch_and_probe 接受的类型。
|
||||
///
|
||||
/// Settings.vue 存的 provider_type 是 "openai_compat" / "anthropic"(对齐 build_provider 工厂),
|
||||
/// 而 model_fetch::fetch_and_probe 分派用 "openai_compat" / "anthropic_compat"。
|
||||
/// 两个工厂入口类型语义一致(anthropic 协议),仅命名不同,这里收敛归一。
|
||||
fn normalize_provider_type_for_fetch(provider_type: &str) -> String {
|
||||
match provider_type {
|
||||
"anthropic" => "anthropic_compat".to_string(),
|
||||
other => other.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试连接并拉取厂商模型列表(F-01 阶段5)
|
||||
///
|
||||
/// 流程:
|
||||
/// 1. DB 取 AiProviderRecord(get_by_id)
|
||||
/// 2. FR-S1:经 resolve_provider_secret 内存解析真实 api_key(keyring 优先 fallback DB,
|
||||
/// 绝不进日志/返回值/错误信息)
|
||||
/// 3. provider_type 归一(anthropic→anthropic_compat)+ base_url + api_key 调
|
||||
/// df_ai::model_fetch::fetch_and_probe → Vec<ModelConfig>(每个模型名已探测出 4 维度)
|
||||
/// 4. 写回 AiProviderRecord.model_configs(update_full)→ 返回 Vec<ModelConfig>
|
||||
///
|
||||
/// ModelConfig 本就无 api_key 字段,返回值天然不含密钥(FR-S1 闭环)。
|
||||
#[tauri::command]
|
||||
pub async fn ai_fetch_models(
|
||||
state: State<'_, AppState>,
|
||||
provider_id: String,
|
||||
) -> Result<Vec<ModelConfig>, String> {
|
||||
let provider = state
|
||||
.ai_providers
|
||||
.get_by_id(&provider_id)
|
||||
.await
|
||||
.map_err(err_str)?
|
||||
.ok_or_else(|| format!("提供商不存在: {}", provider_id))?;
|
||||
|
||||
// FR-S1:内存解析密钥,绝不外泄(不入日志/返回值/错误信息)
|
||||
let api_key = df_storage::secret::resolve_provider_secret(&provider);
|
||||
let fetch_type = normalize_provider_type_for_fetch(&provider.provider_type);
|
||||
|
||||
let configs = df_ai::model_fetch::fetch_and_probe(&fetch_type, &provider.base_url, &api_key)
|
||||
.await
|
||||
.map_err(err_str)?;
|
||||
|
||||
// 写回 model_configs(更新 updated_at)
|
||||
let mut updated = provider.clone();
|
||||
updated.model_configs = configs.clone();
|
||||
updated.updated_at = now_millis();
|
||||
state
|
||||
.ai_providers
|
||||
.update_full(&updated)
|
||||
.await
|
||||
.map_err(err_str)?;
|
||||
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
/// 单模型探测(F-01 阶段5):纯 CPU 启发式 + 预设表,无网络。
|
||||
///
|
||||
/// 用途:拉取后用户手动补一个模型名、或想重探某模型的能力维度。
|
||||
/// 直接调 df_ai::model_probe::probe(&model_id),返回填充了 probe_source 的 ModelConfig。
|
||||
#[tauri::command]
|
||||
pub async fn ai_probe_model(
|
||||
_state: State<'_, AppState>,
|
||||
model_id: String,
|
||||
) -> Result<ModelConfig, String> {
|
||||
Ok(df_ai::model_probe::probe(&model_id))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 对话管理
|
||||
// ============================================================
|
||||
|
||||
@@ -122,6 +122,9 @@ pub fn run() {
|
||||
commands::ai::ai_save_provider,
|
||||
commands::ai::ai_set_provider,
|
||||
commands::ai::ai_delete_provider,
|
||||
// F-01 阶段5:测试连接拉取模型列表 + 单模型探测
|
||||
commands::ai::ai_fetch_models,
|
||||
commands::ai::ai_probe_model,
|
||||
// AI 对话管理
|
||||
commands::ai::ai_conversation_create,
|
||||
commands::ai::ai_conversation_list,
|
||||
|
||||
Reference in New Issue
Block a user