AI助手优化-人设与会话绑定
This commit is contained in:
@@ -11,53 +11,56 @@ final aiPersonasProvider = FutureProvider.autoDispose<List<AiPersonaItem>>((ref)
|
||||
return ref.watch(aiApiProvider).personas();
|
||||
});
|
||||
|
||||
/// 当前选中的人设。
|
||||
/// **新对话时预选哪个人设**的偏好。null = 内置默认人设。
|
||||
///
|
||||
/// **不设 autoDispose**:理由和 [SelectedAiConfigNotifier] 一样 ——
|
||||
/// 选完回对话页要立刻生效,切页面不该丢。
|
||||
class SelectedAiPersonaNotifier extends Notifier<int?> {
|
||||
/// ⚠️ 注意它**不是**「当前正在用的人设」—— 那是 `AssistantState.personaId`,
|
||||
/// 在新建会话时定下来、之后不再变(见 `ai_provider.dart` 里的说明)。
|
||||
///
|
||||
/// 两者的分工:
|
||||
/// · 这个 = 「上次选了谁」,只用来在新对话弹层里打个勾,省得每次重选
|
||||
/// · 会话的 personaId = 「这次对话实际在用谁」,气泡、prompt 都看它
|
||||
///
|
||||
/// 会不一样:用户拿人设 A 聊过一轮,之后新对话选了 B —— 那条旧会话的
|
||||
/// 历史消息仍然该显示 A 的头像和 A 的记录。
|
||||
///
|
||||
/// 名字里刻意没有「selected」这个词:AI 设置页**不提供**「选说话的人」这个动作,
|
||||
/// 选人只发生在新建会话那一步。用「selected」会让人以为设置页能改。
|
||||
class PreferredPersonaNotifier extends Notifier<int?> {
|
||||
@override
|
||||
int? build() {
|
||||
// 存的是「真实 id」,null = 用内置默认。
|
||||
// 存的是「真实 id」,null = 内置默认。
|
||||
// 默认项在列表里的 id 也是 null,两边语义天然一致。
|
||||
return LocalStorage.instance.getAiPersonaId();
|
||||
}
|
||||
|
||||
void select(int? id) {
|
||||
void remember(int? id) {
|
||||
state = id;
|
||||
LocalStorage.instance.saveAiPersonaId(id);
|
||||
}
|
||||
|
||||
/// 被记住的那条人设删了 —— 忘掉它,别留着指向不存在的 id
|
||||
void forget() => remember(null);
|
||||
}
|
||||
|
||||
final selectedAiPersonaProvider =
|
||||
NotifierProvider<SelectedAiPersonaNotifier, int?>(SelectedAiPersonaNotifier.new);
|
||||
final preferredPersonaProvider =
|
||||
NotifierProvider<PreferredPersonaNotifier, int?>(PreferredPersonaNotifier.new);
|
||||
|
||||
/// 从人设列表里挑出当前选中的那一条。
|
||||
/// 按 id 找人设。聊天气泡用它取「当前会话绑的那个人」的头像。
|
||||
///
|
||||
/// 三种情况都要兜住:
|
||||
/// · 还没选过 → 用列表第一项(也就是「默认」)
|
||||
/// · 选过但那条被删了 → 退回第一项,并顺手把本地选择改掉
|
||||
/// · 列表还没加载出来 → 返回 null,界面显示「--」
|
||||
/// 直接读会话的 personaId 去找,而不是用全局偏好 —— 会话锁定了谁,
|
||||
/// 气泡上就该显示谁,和那段回答实际用的 prompt 是同一套。
|
||||
///
|
||||
/// 注意这里和服务端的降级是**两套独立的安全网**:服务端拿到过期 id 也会
|
||||
/// 退回内置默认,所以就算这里没来得及纠正,对话也不会挂。
|
||||
final activeAiPersonaProvider = Provider.autoDispose<AiPersonaItem?>((ref) {
|
||||
final personas = ref.watch(aiPersonasProvider).value;
|
||||
if (personas == null || personas.isEmpty) return null;
|
||||
|
||||
final selectedId = ref.watch(selectedAiPersonaProvider);
|
||||
if (selectedId == null) return personas.first;
|
||||
|
||||
for (final item in personas) {
|
||||
if (item.selectionKey == selectedId) return item;
|
||||
/// id 为 null(内置默认人设)或找不到(人设被删)时返回 null,
|
||||
/// 调用方据此显示默认图标。
|
||||
final personaByIdProvider =
|
||||
Provider.autoDispose.family<AiPersonaItem?, int?>((ref, id) {
|
||||
if (id == null) return null;
|
||||
final list = ref.watch(aiPersonasProvider).value;
|
||||
if (list == null) return null;
|
||||
for (final p in list) {
|
||||
if (p.id == id) return p;
|
||||
}
|
||||
|
||||
// 选中的那条已经不存在了(被删了)。退回默认。
|
||||
// 放在微任务里改 state —— build 过程中不能同步改另一个 provider。
|
||||
Future.microtask(() {
|
||||
ref.read(selectedAiPersonaProvider.notifier).select(null);
|
||||
});
|
||||
return personas.first;
|
||||
return null;
|
||||
});
|
||||
|
||||
/// 内置默认人设文本,供编辑页「恢复默认」按钮使用。
|
||||
|
||||
Reference in New Issue
Block a user