feat(m174): 助手(AI)提供商配置真接线 + 「测试连接」
设置页新增第 6 组「助手」:提供商四态 / 端点 base_url / 模型名 / API 密钥(默认掩码, 点「显示」才明文)+ 保存 + 测试连接,四类失败(鉴权 / 模型不存在 / 不可达 / 超时)各自 一句文案 + 目标与耗时。 取舍三条: 1. 读写走 `core::setting` 的 schema API(`SettingStore::load` 现读 → `set` → `save`), 不经 `AppCommand::SetSetting`——那条指令是可序列化 DTO,`ai.api_key` 走它等于让明文进 事件流/日志。与 `SettingsBridge::persist` 写外观是同一条做法。 2. 「测试连接」不另写一套 HTTP:`unlucky_cli::commands::agent::probe` 现读设置文件、 交给 `RigProvider`(真 provider)发一次最小补全,带 8 秒时限;分类只看错误文本 (rig 只给 Display),四类各有一条真 TCP 假服务器用例钉住。 3. `ProviderKind` 补 `from_setting` / `setting_value`:serde 的 `rename_all = snake_case` 产出 `open_ai_compatible`,与 schema 的 `openai_compatible` 不是同一个词——这正是 M174 之前「agent 侧一次都没读过设置」的根因。 守卫(都做变异验证,见 mission 报告):密钥不出 `Debug`/`detail`/DTO;未配置时工厂构造 计数为 0;改完 base_url 后探测打到新地址;空/非法值被 schema 拦下且文件不动。 i18n:49 条中英各一(金表按 `Key::ALL` 位置插入);presenter 的 ai_group 补上 schema 允许 的 `disabled` 选项与一行「已配置 N 字符」的只读状态(仍然不显示值)。
This commit is contained in:
1 parent
82f3bbbff6
commit
9cb605f316
23 files changed
+2939
-73
No files matched your search
@@ -28,7 +28,8 @@
|
||||
//! | Appearance | [`appearance`] | language+appearance 线(真控件已在 `pages/appearance/section.rs`) |
|
||||
//! | Java | [`java`] | java 线 |
|
||||
//! | Downloads | [`download`] | download 线 |
|
||||
//! | (页面上还没有这个分组) | [`ai_about`] | ai+about 线:先建分组再接线 |
|
||||
//! | Updates | [`update`] | update 线(M159,按下标分发) |
|
||||
//! | Assistant | [`ai_about`](正文在 [`ai`]) | ai 线(M174,按下标分发:本组要 `Lang`) |
|
||||
//! | 页头 / 侧列 / 滚动区 / 演示卡片 / 空态位 | [`frame`] | 谁都不该改(改了就是改版面) |
|
||||
//!
|
||||
//! ## 测试也按 section 分(M100)
|
||||
@@ -93,9 +94,11 @@
|
||||
//! (`split_keeps_every_translation` 逐条比 key + 英/中译文)。**注意**:`catalog.rs` 的 `Key`
|
||||
//! 枚举由 `catalog!` 宏从各域文件累积生成,加 key **不用**改 `catalog.rs`,也不用改
|
||||
//! `catalog/settings/mod.rs`(五个域已全部接进装配链)。
|
||||
//! 2. 本文件的 [`GROUPS`] —— 只有 ai+about 线**上第 5 组**时加一行(页面上多一组本来就是整页的变化)。
|
||||
//! 3. `tests/mod.rs` —— ai+about 线新建 `tests/ai_section.rs` 时要加一行 `mod ai_section;`
|
||||
//! (测试文件不挂 `mod` 就不编译),以及「左列顺序」那条字面量断言会连带红,需要人确认。
|
||||
//! 2. 本文件的 [`GROUPS`] —— 加一整组时加一行(M174 的助手组就是这么来的:`GROUPS` 与
|
||||
//! [`GROUP_LABELS`] 各加一行、长度 +1,`tests/mod.rs` 的「左列顺序」随之更新)。
|
||||
//! 3. `tests/mod.rs` —— 「左列顺序」那条字面量断言会连带红,需要人确认(M174 就是把它加了一个
|
||||
//! `"Assistant"`)。加分组自己的测试文件时还要在 `tests/mod.rs` 顶部挂一行 `mod <名>;`;
|
||||
//! M174 的用例走的是组内路径(`pages/settings/ai/tests.rs`),所以没有新增挂载行。
|
||||
//! 4. **读页外真值**(r2 复审 P1 补的)—— [`States`] 只有 `appearance` + `groups` 两格,组正文读不到
|
||||
//! `pages::Model`(「当前语言」的值在 `Model.lang`,`pages/model.rs`),也读不到 presenter 的设置
|
||||
//! 快照。两条路的**真实成本**(`States` 没有 `Default`,加字段必然 E0063,已实测):
|
||||
@@ -132,6 +135,7 @@
|
||||
// 四条填页线的文件要能被**各自那条落点**(`app/update/settings/<线>.rs`)与状态架子
|
||||
// (`state::Groups`)命名,所以是 `pub(crate)`:最小面,不是 crate 公开 API。
|
||||
// 分组正文文件 `appearance` / `frame` / `general` 只在设置页内部用,保持私有。
|
||||
pub(crate) mod ai;
|
||||
pub(crate) mod ai_about;
|
||||
mod appearance;
|
||||
pub(crate) mod download;
|
||||
@@ -215,7 +219,7 @@ struct Group {
|
||||
/// 分组注册表:**顺序 = 左列自上而下**,也是 `crate::pages::appearance::APPEARANCE_GROUP` 的下标来源。
|
||||
///
|
||||
/// 每一项都指向它自己文件里的 `GROUP`(M99 之前是四份字面量挤在本文件里)。
|
||||
const GROUPS: [Group; 5] = [
|
||||
const GROUPS: [Group; 6] = [
|
||||
general::GROUP,
|
||||
appearance::GROUP,
|
||||
java::GROUP,
|
||||
@@ -223,6 +227,8 @@ const GROUPS: [Group; 5] = [
|
||||
// M159:自更新只**追加**在末尾(插在中间会让 `APPEARANCE_GROUP` / `GENERAL_GROUP` 两个下标
|
||||
// 全部失效,`tests/mod.rs` 的「左列顺序」那条也会红)。
|
||||
update::GROUP,
|
||||
// M174:助手(AI)配置。同样只追加(下标 = `ai_about::AI_GROUP`),正文走按下标分发。
|
||||
ai_about::GROUP,
|
||||
];
|
||||
|
||||
/// 演示选中的分组(没有真实外观状态时——`pages_demo` harness——用它)。
|
||||
@@ -241,13 +247,15 @@ const GENERAL_GROUP: usize = 0;
|
||||
///
|
||||
/// 为什么不放进 [`Group`]:那会让另外三组的 `GROUP` 字面量(各自文件、各自填页线)编译不过——
|
||||
/// 与 [`GENERAL_GROUP`] 同一条理由。`Group.name` 因此退化为**id**(测试按它认组;显示走这张表)。
|
||||
pub(super) const GROUP_LABELS: [unlucky_i18n::Key; 5] = [
|
||||
pub(super) const GROUP_LABELS: [unlucky_i18n::Key; 6] = [
|
||||
unlucky_i18n::Key::SettingsGroupGeneral,
|
||||
unlucky_i18n::Key::SettingsGroupAppearance,
|
||||
unlucky_i18n::Key::SettingsGroupJava,
|
||||
unlucky_i18n::Key::SettingsGroupDownloads,
|
||||
// M159
|
||||
unlucky_i18n::Key::SettingsGroupUpdate,
|
||||
// M174
|
||||
unlucky_i18n::Key::SettingsGroupAssistant,
|
||||
];
|
||||
|
||||
/// 「更新」分组在 [`GROUPS`] 里的下标(真源在 `settings/update.rs`,这里只转发)。
|
||||
@@ -256,6 +264,13 @@ pub(super) const GROUP_LABELS: [unlucky_i18n::Key; 5] = [
|
||||
/// 所以由 `view` 的 `update` 形参一路递到下标的 [`form`] 分发点,`GROUP.body` 保持 `None`。
|
||||
const UPDATE_GROUP: usize = update::UPDATE_GROUP;
|
||||
|
||||
/// 「助手(AI)」分组在 [`GROUPS`] 里的下标(真源在 `settings/ai_about.rs`,这里只转发)。
|
||||
///
|
||||
/// 与 [`GENERAL_GROUP`] / [`UPDATE_GROUP`] 同一条做法:本组要画中英两套文案(`Lang`)与
|
||||
/// 「测试连接」的结论,`Group.body` 的签名里没有 `Lang`,所以由 `view` 的 `language` 形参一路
|
||||
/// 递到 [`form`] 的分发点,`GROUP.body` 保持 `None`。
|
||||
const AI_GROUP: usize = ai_about::AI_GROUP;
|
||||
|
||||
/// 整页。
|
||||
///
|
||||
/// `states` 是「画一帧时那份只读状态」:[`States::appearance`] 是 M93 接进来的外观运行期状态
|
||||
@@ -321,6 +336,9 @@ fn form<'a>(
|
||||
} else if index == UPDATE_GROUP {
|
||||
// M159:更新组读 presenter 快照(`view` 的 `update` 形参),与 General 同一条按下标分发。
|
||||
update::body(ui, update, language.effective)
|
||||
} else if index == AI_GROUP {
|
||||
// M174:助手组要 i18n 文案与「测试连接」结论,与 General / Update 同一条按下标分发。
|
||||
ai::view::body(ui, states, language.effective)
|
||||
} else {
|
||||
group
|
||||
.body
|
||||
|
||||
Reference in new issue
Block a user