81 lines
3.2 KiB
Dart
81 lines
3.2 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../api/ai_api.dart';
|
||
import '../core/storage/local_storage.dart';
|
||
import '../models/ai_persona.dart';
|
||
|
||
/// 可选的人设列表(含虚拟的「默认」项)
|
||
///
|
||
/// 与 [aiConfigsProvider] 完全平行:一个拉模型配置,一个拉人设。
|
||
final aiPersonasProvider = FutureProvider.autoDispose<List<AiPersonaItem>>((ref) {
|
||
return ref.watch(aiApiProvider).personas();
|
||
});
|
||
|
||
/// **新对话时预选哪个人设**的偏好。null = 内置默认人设。
|
||
///
|
||
/// ⚠️ 注意它**不是**「当前正在用的人设」—— 那是 `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,两边语义天然一致。
|
||
return LocalStorage.instance.getAiPersonaId();
|
||
}
|
||
|
||
void remember(int? id) {
|
||
state = id;
|
||
LocalStorage.instance.saveAiPersonaId(id);
|
||
}
|
||
|
||
/// 被记住的那条人设删了 —— 忘掉它,别留着指向不存在的 id
|
||
void forget() => remember(null);
|
||
}
|
||
|
||
final preferredPersonaProvider =
|
||
NotifierProvider<PreferredPersonaNotifier, int?>(PreferredPersonaNotifier.new);
|
||
|
||
/// 按 id 找人设。聊天气泡用它取「当前会话绑的那个人」的头像。
|
||
///
|
||
/// 直接读会话的 personaId 去找,而不是用全局偏好 —— 会话锁定了谁,
|
||
/// 气泡上就该显示谁,和那段回答实际用的 prompt 是同一套。
|
||
///
|
||
/// 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;
|
||
}
|
||
return null;
|
||
});
|
||
|
||
/// 内置默认人设文本,供编辑页「恢复默认」按钮使用。
|
||
///
|
||
/// 从列表的默认项里取(服务端下发),拿不到时返回 null ——
|
||
/// 这时按钮会提示「还没加载出来」,而不是塞一份 App 里硬编码的文本进去
|
||
/// (那样两边迟早不一致)。
|
||
final defaultPersonaPromptProvider = Provider.autoDispose<String?>((ref) {
|
||
final personas = ref.watch(aiPersonasProvider).value;
|
||
if (personas == null) return null;
|
||
for (final p in personas) {
|
||
if (p.isDefault && (p.defaultPrompt?.isNotEmpty ?? false)) {
|
||
return p.defaultPrompt;
|
||
}
|
||
}
|
||
return null;
|
||
});
|