马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,746 @@
import 'package:ainoval/models/user_ai_model_config_model.dart';
import 'package:ainoval/models/ai_model_group.dart';
import 'package:ainoval/models/model_info.dart'; // Import ModelInfo
import 'package:ainoval/services/api_service/repositories/user_ai_model_config_repository.dart';
import 'package:ainoval/utils/logger.dart';
import 'package:bloc/bloc.dart';
import 'package:collection/collection.dart'; // For firstWhereOrNull
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; // For ValueGetter
part 'ai_config_event.dart';
part 'ai_config_state.dart';
class AiConfigBloc extends Bloc<AiConfigEvent, AiConfigState> {
AiConfigBloc({required UserAIModelConfigRepository repository})
: _repository = repository,
super(const AiConfigState()) {
on<LoadAiConfigs>(_onLoadAiConfigs);
on<ResetAiConfigs>(_onResetAiConfigs);
on<LoadAvailableProviders>(_onLoadAvailableProviders);
on<LoadModelsForProvider>(_onLoadModelsForProvider);
on<AddAiConfig>(_onAddAiConfig);
on<UpdateAiConfig>(_onUpdateAiConfig);
on<DeleteAiConfig>(_onDeleteAiConfig);
on<ValidateAiConfig>(_onValidateAiConfig);
on<SetDefaultAiConfig>(_onSetDefaultAiConfig);
on<ClearProviderModels>(_onClearProviderModels);
on<GetProviderDefaultConfig>(_onGetProviderDefaultConfig);
on<LoadApiKeyForConfig>(_onLoadApiKeyForConfig);
on<LoadProviderCapability>(_onLoadProviderCapability);
on<TestApiKey>(_onTestApiKey);
on<ClearApiKeyTestError>(_onClearApiKeyTestError);
on<ClearModelsCache>(_onClearModelsCache);
on<AddCustomModelAndValidate>(_onAddCustomModelAndValidate);
}
final UserAIModelConfigRepository _repository;
// 添加缓存机制
DateTime? _lastConfigsLoadTime;
static const Duration _cacheValidDuration = Duration(minutes: 5);
// 记录上一次加载配置对应的用户,用于跨用户时强制刷新
String? _lastLoadedUserId;
// 添加模型列表缓存机制
Map<String, DateTime> _modelsCacheTime = {};
static const Duration _modelsCacheValidDuration = Duration(minutes: 10);
// 添加提供商列表缓存机制
DateTime? _lastProvidersLoadTime;
static const Duration _providersCacheDuration = Duration(hours: 1);
bool get _shouldRefreshConfigs {
if (_lastConfigsLoadTime == null) return true;
return DateTime.now().difference(_lastConfigsLoadTime!) > _cacheValidDuration;
}
bool get _shouldRefreshProviders {
if (_lastProvidersLoadTime == null) return true;
return DateTime.now().difference(_lastProvidersLoadTime!) > _providersCacheDuration;
}
// 检查特定提供商的模型列表缓存是否有效
bool _shouldRefreshModels(String provider) {
// 如果状态中没有该提供商的模型数据,需要加载
if (!state.modelGroups.containsKey(provider) ||
state.modelGroups[provider]?.allModelsInfo.isEmpty == true) {
return true;
}
// 检查缓存时间
final lastLoadTime = _modelsCacheTime[provider];
if (lastLoadTime == null) {
// 模型数据已存在但没有记录时间戳,认为仍然有效,补记录当前时间
_modelsCacheTime[provider] = DateTime.now();
return false;
}
return DateTime.now().difference(lastLoadTime) > _modelsCacheValidDuration;
}
/// Helper方法根据配置列表重新构建providerDefaultConfigs
Map<String, UserAIModelConfigModel> _buildProviderDefaultConfigs(
List<UserAIModelConfigModel> configs) {
final Map<String, UserAIModelConfigModel> providerDefaultConfigs = {};
// 按提供商分组
final configsByProvider = <String, List<UserAIModelConfigModel>>{};
for (final config in configs) {
if (!configsByProvider.containsKey(config.provider)) {
configsByProvider[config.provider] = [];
}
configsByProvider[config.provider]!.add(config);
}
// 为每个提供商选择一个默认配置
configsByProvider.forEach((provider, providerConfigs) {
// 优先选择默认配置,其次是已验证的配置,最后选择第一个配置
final defaultConfig = providerConfigs.firstWhere(
(c) => c.isDefault,
orElse: () => providerConfigs.firstWhere(
(c) => c.isValidated,
orElse: () => providerConfigs.first,
),
);
providerDefaultConfigs[provider] = defaultConfig;
});
return providerDefaultConfigs;
}
Future<void> _onLoadAiConfigs(
LoadAiConfigs event, Emitter<AiConfigState> emit) async {
// 如果用户已切换,强制刷新缓存与状态
if (_lastLoadedUserId != null && _lastLoadedUserId != event.userId) {
_lastConfigsLoadTime = null;
}
// 检查缓存是否有效
if (!_shouldRefreshConfigs && state.configs.isNotEmpty) {
AppLogger.d('AiConfigBloc', '使用缓存的配置数据,跳过重新加载');
return;
}
emit(state.copyWith(status: AiConfigStatus.loading));
try {
final configs =
await _repository.listConfigurations(userId: event.userId);
_lastConfigsLoadTime = DateTime.now(); // 更新缓存时间
// 按提供商分组用户配置
final providerDefaultConfigs = _buildProviderDefaultConfigs(configs);
emit(state.copyWith(
status: AiConfigStatus.loaded,
configs: configs,
providerDefaultConfigs: providerDefaultConfigs,
errorMessage: () => null, // Clear previous error
));
// 记录当前加载用户
_lastLoadedUserId = event.userId;
AppLogger.i('AiConfigBloc', '配置加载成功,共${configs.length}个配置,已缓存');
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '加载配置失败', e, stackTrace);
emit(state.copyWith(
status: AiConfigStatus.error, errorMessage: () => e.toString()));
}
}
// 重置事件:清空状态与所有相关缓存(用于登出/切换账号)
void _onResetAiConfigs(ResetAiConfigs event, Emitter<AiConfigState> emit) {
_lastConfigsLoadTime = null;
_lastProvidersLoadTime = null;
_modelsCacheTime.clear();
_lastLoadedUserId = null;
emit(const AiConfigState());
AppLogger.i('AiConfigBloc', '已重置AI配置状态与缓存');
}
Future<void> _onLoadAvailableProviders(
LoadAvailableProviders event, Emitter<AiConfigState> emit) async {
// 如果已有缓存且未过期,直接返回
if (!_shouldRefreshProviders && state.availableProviders.isNotEmpty) {
AppLogger.d('AiConfigBloc', '使用缓存的提供商列表,跳过重新加载');
return;
}
try {
final providers = await _repository.listAvailableProviders();
_lastProvidersLoadTime = DateTime.now();
emit(state.copyWith(
availableProviders: providers,
errorMessage: () => null,
));
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '加载提供商失败', e, stackTrace);
emit(state.copyWith(errorMessage: () => '加载提供商列表失败: \\${e.toString()}'));
}
}
Future<void> _onLoadModelsForProvider(
LoadModelsForProvider event, Emitter<AiConfigState> emit) async {
// 检查缓存是否有效
if (!_shouldRefreshModels(event.provider)) {
AppLogger.d('AiConfigBloc', '使用缓存的模型数据,跳过重新加载: provider=${event.provider}');
// 更新selectedProviderForModels以确保UI正确显示
final cachedModelGroup = state.modelGroups[event.provider];
if (cachedModelGroup != null) {
emit(state.copyWith(
selectedProviderForModels: event.provider,
modelsForProviderInfo: cachedModelGroup.allModelsInfo,
));
// 仍然触发GetProviderDefaultConfig以确保默认配置正确加载
add(GetProviderDefaultConfig(provider: event.provider));
}
return;
}
emit(state.copyWith(
modelsForProviderInfo: [],
selectedProviderForModels: event.provider,
apiKeyTestSuccessProviderClearable: () => null,
apiKeyTestErrorClearable: () => null,
));
try {
final models = await _repository.listModelsForProvider(event.provider);
AppLogger.i('AiConfigBloc', '成功获取模型列表provider=${event.provider},模型数量=${models.length}');
// 更新缓存时间
_modelsCacheTime[event.provider] = DateTime.now();
// Use the new factory for ModelInfo list
final modelGroup = AIModelGroup.fromModelInfoList(event.provider, models);
final updatedModelGroups = Map<String, AIModelGroup>.from(state.modelGroups);
updatedModelGroups[event.provider] = modelGroup;
emit(state.copyWith(
modelsForProviderInfo: models,
modelGroups: updatedModelGroups, // Update model groups
errorMessage: () => null
));
AppLogger.i('AiConfigBloc', '模型加载完成已缓存触发GetProviderDefaultConfigprovider=${event.provider}');
add(GetProviderDefaultConfig(provider: event.provider));
} catch (e, stackTrace) {
AppLogger.e(
'AiConfigBloc', '加载模型失败 for ${event.provider}', e, stackTrace);
AppLogger.w('AiConfigBloc', '加载模型失败provider=${event.provider},错误:$e');
emit(state.copyWith(
modelsForProviderInfo: [],
errorMessage: () => '加载模型列表失败: ${e.toString()}'));
}
}
Future<void> _onAddAiConfig(
AddAiConfig event, Emitter<AiConfigState> emit) async {
emit(state.copyWith(
actionStatus: AiConfigActionStatus.loading,
actionErrorMessage: () => null));
try {
AppLogger.i('AiConfigBloc', '开始添加配置: provider=${event.provider}, modelName=${event.modelName}');
final newConfig = await _repository.addConfiguration(
userId: event.userId,
provider: event.provider,
modelName: event.modelName,
alias: event.alias,
apiKey: event.apiKey,
apiEndpoint: event.apiEndpoint,
);
AppLogger.i('AiConfigBloc', '配置添加成功: configId=${newConfig.id}');
// 直接更新列表,避免重复请求
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
currentConfigs.add(newConfig);
// 重新构建providerDefaultConfigs
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
// 使缓存失效,确保下次加载最新数据
_lastConfigsLoadTime = null;
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs,
));
AppLogger.i('AiConfigBloc', '配置列表已更新,避免重复请求');
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '添加配置失败', e, stackTrace);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.error,
actionErrorMessage: () => '添加失败: ${e.toString()}'));
}
}
Future<void> _onUpdateAiConfig(
UpdateAiConfig event, Emitter<AiConfigState> emit) async {
emit(state.copyWith(
actionStatus: AiConfigActionStatus.loading,
actionErrorMessage: () => null));
try {
final updatedConfig = await _repository.updateConfiguration(
userId: event.userId,
configId: event.configId,
alias: event.alias,
apiKey: event.apiKey,
apiEndpoint: event.apiEndpoint,
);
// 更新列表中的特定项
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
final index = currentConfigs.indexWhere((c) => c.id == updatedConfig.id);
if (index != -1) {
currentConfigs[index] = updatedConfig;
// 重新构建providerDefaultConfigs以确保UI正确显示
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs));
} else {
// 如果找不到,最好还是重新加载
emit(state.copyWith(actionStatus: AiConfigActionStatus.success));
add(LoadAiConfigs(userId: event.userId));
}
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '更新配置失败', e, stackTrace);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.error,
actionErrorMessage: () => '更新失败: ${e.toString()}'));
}
}
Future<void> _onDeleteAiConfig(
DeleteAiConfig event, Emitter<AiConfigState> emit) async {
emit(state.copyWith(
actionStatus: AiConfigActionStatus.loading,
actionErrorMessage: () => null));
try {
await _repository.deleteConfiguration(
userId: event.userId, configId: event.configId);
// 从列表中移除
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
currentConfigs.removeWhere((c) => c.id == event.configId);
// 重新构建providerDefaultConfigs以确保UI正确显示
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs));
// 如果删除的是默认配置,可能需要清除默认状态或重新加载以确认新的默认(如果后端自动处理)
// 这里暂时只移除
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '删除配置失败', e, stackTrace);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.error,
actionErrorMessage: () => '删除失败: ${e.toString()}'));
}
}
Future<void> _onValidateAiConfig(
ValidateAiConfig event, Emitter<AiConfigState> emit) async {
try {
AppLogger.i('AiConfigBloc', '开始验证配置: configId=${event.configId}');
emit(state.copyWith(
actionStatus: AiConfigActionStatus.loading,
actionErrorMessage: null,
loadingConfigId: event.configId));
final validatedConfig = await _repository.validateConfiguration(
userId: event.userId, configId: event.configId);
AppLogger.i('AiConfigBloc', '配置验证完成: configId=${event.configId}, isValidated=${validatedConfig.isValidated}');
// 更新列表中的特定项
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
final index =
currentConfigs.indexWhere((c) => c.id == validatedConfig.id);
if (index != -1) {
currentConfigs[index] = validatedConfig;
// 重新构建providerDefaultConfigs以确保UI正确显示
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs,
loadingConfigId: null));
} else {
AppLogger.w('AiConfigBloc', '验证后找不到配置,触发重新加载');
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
loadingConfigId: null));
add(LoadAiConfigs(userId: event.userId));
}
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '验证配置失败', e, stackTrace);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.error,
actionErrorMessage: () => '验证请求失败: ${e.toString()}',
loadingConfigId: null));
}
}
Future<void> _onSetDefaultAiConfig(
SetDefaultAiConfig event, Emitter<AiConfigState> emit) async {
emit(state.copyWith(
actionStatus: AiConfigActionStatus.loading,
actionErrorMessage: () => null));
try {
AppLogger.i('AiConfigBloc', '开始设置默认配置: configId=${event.configId}');
final newDefaultConfig = await _repository.setDefaultConfiguration(
userId: event.userId, configId: event.configId);
// 更新所有配置的默认状态
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
for (int i = 0; i < currentConfigs.length; i++) {
if (currentConfigs[i].id == event.configId) {
currentConfigs[i] = newDefaultConfig;
} else if (currentConfigs[i].isDefault) {
// 取消其他配置的默认状态
currentConfigs[i] = currentConfigs[i].copyWith(isDefault: false);
}
}
// 重新构建providerDefaultConfigs
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
// 使缓存失效
_lastConfigsLoadTime = null;
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs,
));
AppLogger.i('AiConfigBloc', '默认配置设置成功,避免重复请求');
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '设置默认配置失败', e, stackTrace);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.error,
actionErrorMessage: () => '设置默认失败: ${e.toString()}'));
}
}
void _onClearProviderModels(
ClearProviderModels event, Emitter<AiConfigState> emit) {
// 清除模型列表和当前选中的提供商
emit(state.copyWith(
clearModels: true,
// 保留模型分组信息,因为它可能在其他地方被使用
// 如果需要清除特定提供商的模型分组,可以在这里处理
));
}
// 根据provider查找第一个可用的配置用于显示该提供商的API密钥和URL
Future<void> _onGetProviderDefaultConfig(
GetProviderDefaultConfig event, Emitter<AiConfigState> emit) async {
final provider = event.provider;
print('⚠️ 开始处理GetProviderDefaultConfig事件provider=$provider');
// 获取当前状态的providerDefaultConfigs副本
final providerDefaultConfigs = Map<String, UserAIModelConfigModel>.from(state.providerDefaultConfigs);
// 从已加载的配置中查找
final providerConfigs = state.configs.where((c) => c.provider == provider).toList();
print('⚠️ 查找provider=$provider的配置,找到${providerConfigs.length}个配置');
if (providerConfigs.isEmpty) {
print('⚠️ 没有找到provider=$provider的配置');
// 没有找到该提供商的配置从Map中移除这个提供商的配置如果有
if (providerDefaultConfigs.containsKey(provider)) {
providerDefaultConfigs.remove(provider);
emit(state.copyWith(
providerDefaultConfigs: providerDefaultConfigs,
));
print('⚠️ 已从providerDefaultConfigs中移除provider=$provider的配置');
}
return;
}
// 首先寻找默认的
final defaultConfig = providerConfigs.firstWhere(
(c) => c.isDefault,
orElse: () => providerConfigs.firstWhere(
(c) => c.isValidated,
orElse: () => providerConfigs.first,
),
);
print('⚠️ 找到provider=$provider的默认配置id=${defaultConfig.id}apiEndpoint=${defaultConfig.apiEndpoint}hasApiKey=${defaultConfig.apiKey != null}');
// 更新或添加该提供商的默认配置
providerDefaultConfigs[provider] = defaultConfig;
// 更新状态
emit(state.copyWith(
providerDefaultConfigs: providerDefaultConfigs,
));
print('⚠️ 已更新状态中的providerDefaultConfigs当前包含的提供商${providerDefaultConfigs.keys.join(", ")}');
}
// 处理加载API密钥的事件
Future<void> _onLoadApiKeyForConfig(
LoadApiKeyForConfig event, Emitter<AiConfigState> emit) async {
try {
// 从已加载的配置中查找
final config = state.configs.firstWhereOrNull(
(config) => config.id == event.configId
);
if (config != null && config.apiKey != null) {
// 如果已加载的配置中有API密钥直接使用
// event.onApiKeyLoaded(config.apiKey!); // Commenting out: ValueGetter<void> takes no arguments
print("API Key found in state for ${event.configId}");
// TODO: Decide how to actually return/use this key - maybe emit a state?
return;
}
// 如果没有找到配置或者没有API密钥提示用户手动输入
// event.onApiKeyLoaded("请手动输入API密钥"); // Commenting out: ValueGetter<void> takes no arguments
print("API Key NOT found in state for ${event.configId}");
// TODO: Decide how to handle missing key - maybe emit an error state?
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '获取API密钥失败', e, stackTrace);
// 如果失败,返回一个错误提示
// event.onApiKeyLoaded("获取失败,请手动输入"); // Commenting out: ValueGetter<void> takes no arguments
print("Error loading API Key for ${event.configId}: $e");
// TODO: Decide how to handle error - maybe emit an error state?
}
}
// --- Handlers for New Events ---
Future<void> _onLoadProviderCapability(
LoadProviderCapability event, Emitter<AiConfigState> emit) async {
// Reset previous capability and test status for the new provider
emit(state.copyWith(
providerCapabilityClearable: () => null,
isTestingApiKey: false,
apiKeyTestSuccessProviderClearable: () => null,
apiKeyTestErrorClearable: () => null,
));
try {
// 调用repository方法获取提供商能力
final capability = await _repository.getProviderCapability(event.providerName);
AppLogger.i('AiConfigBloc', '加载提供商 ${event.providerName} 能力成功: $capability');
emit(state.copyWith(providerCapability: capability));
// --- 修改开始 ---
// bool shouldLoadWithKey = false; // 已不再使用
UserAIModelConfigModel? defaultConfig;
// 优先从 providerDefaultConfigs 获取,因为它是为这个场景设计的
defaultConfig = state.providerDefaultConfigs[event.providerName];
// 如果默认配置里没key再尝试从完整列表里捞一个有效的 (可能不是最优选择,但作为后备)
// if (defaultConfig == null || defaultConfig.apiKey == null || defaultConfig.apiKey!.isEmpty) {
// final providerConfigs = state.configs.where((c) => c.provider == event.providerName).toList();
// if (providerConfigs.isNotEmpty) {
// defaultConfig = providerConfigs.firstWhere(
// (c) => c.isDefault && c.apiKey != null && c.apiKey!.isNotEmpty,
// orElse: () => providerConfigs.firstWhere(
// (c) => c.isValidated && c.apiKey != null && c.apiKey!.isNotEmpty,
// orElse: () => providerConfigs.firstWhere(
// (c) => c.apiKey != null && c.apiKey!.isNotEmpty,
// orElse: () => providerConfigs.first // Last resort: first config even without key
// )
// )
// );
// }
// }
if (capability == ModelListingCapability.listingWithKey) {
// 检查找到的配置(优先是 providerDefaultConfigs 里的)是否有有效的 API Key
if (defaultConfig != null && defaultConfig.apiKey != null && defaultConfig.apiKey!.isNotEmpty) {
// 注释掉自动验证逻辑避免在新建模式下自动验证API Key
// shouldLoadWithKey = true;
AppLogger.i('AiConfigBloc', 'Provider ${event.providerName} 需要 Key找到已配置的 Key但不自动验证将加载默认模型列表');
} else {
AppLogger.i('AiConfigBloc', 'Provider ${event.providerName} 需要 Key但未找到带 Key 的默认/有效配置,将加载默认模型');
}
} else {
AppLogger.i('AiConfigBloc', 'Provider ${event.providerName} 不需要 Key 或不支持列表,将加载默认模型');
}
// 清除之前的测试状态和错误信息,避免残留
emit(state.copyWith(
apiKeyTestSuccessProviderClearable: () => null,
apiKeyTestErrorClearable: () => null,
isTestingApiKey: false // 不自动测试API Key
));
// 统一使用LoadModelsForProvider加载模型列表不自动验证API Key
AppLogger.i('AiConfigBloc', '触发加载 ${event.providerName} 的默认模型列表 (LoadModelsForProvider)');
add(LoadModelsForProvider(provider: event.providerName));
// --- 修改结束 ---
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '加载提供商 ${event.providerName} 能力失败', e, stackTrace);
emit(state.copyWith(errorMessage: () => '加载提供商能力失败: ${e.toString()}'));
// 即使能力加载失败,也尝试加载默认模型列表,避免界面空白
AppLogger.w('AiConfigBloc', '能力加载失败,仍尝试加载 ${event.providerName} 的默认模型列表');
add(LoadModelsForProvider(provider: event.providerName));
}
}
Future<void> _onTestApiKey(
TestApiKey event, Emitter<AiConfigState> emit) async {
emit(state.copyWith(
isTestingApiKey: true,
apiKeyTestSuccessProviderClearable: () => null,
apiKeyTestErrorClearable: () => null,
));
try {
final models = await _repository.listModelsWithApiKey(
provider: event.providerName,
apiKey: event.apiKey,
apiEndpoint: event.apiEndpoint,
);
AppLogger.i('AiConfigBloc', '测试 API Key 成功 for ${event.providerName}, 获取到 ${models.length} 个模型');
// 更新缓存时间
_modelsCacheTime[event.providerName] = DateTime.now();
// Use the new factory for ModelInfo list
final modelGroup = AIModelGroup.fromModelInfoList(event.providerName, models);
final updatedModelGroups = Map<String, AIModelGroup>.from(state.modelGroups);
updatedModelGroups[event.providerName] = modelGroup;
emit(state.copyWith(
isTestingApiKey: false,
apiKeyTestSuccessProvider: event.providerName,
modelsForProviderInfo: models,
modelGroups: updatedModelGroups, // Update model groups
selectedProviderForModels: event.providerName,
));
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '测试 API Key 异常 for ${event.providerName}', e, stackTrace);
emit(state.copyWith(
isTestingApiKey: false,
apiKeyTestError: 'API Key 测试失败: ${e.toString()}',
modelsForProviderInfo: [],
));
}
}
// Handler to clear the API key test error
void _onClearApiKeyTestError(
ClearApiKeyTestError event, Emitter<AiConfigState> emit) {
// Use ValueGetter to explicitly set the error to null
emit(state.copyWith(apiKeyTestErrorClearable: () => null));
}
// Optional: Modify _onLoadModelsForProvider if needed
// Example: Reset API key test status when models are loaded without a key test
// Future<void> _onLoadModelsForProvider(
// LoadModelsForProvider event, Emitter<AiConfigState> emit) async {
// emit(state.copyWith(
// modelsForProvider: [],
// selectedProviderForModels: event.provider,
// // Reset test status if loading models without key
// apiKeyTestSuccessProviderClearable: () => null,
// apiKeyTestErrorClearable: () => null
// ));
// // ... rest of the existing logic ...
// }
Future<void> _onAddCustomModelAndValidate(
AddCustomModelAndValidate event, Emitter<AiConfigState> emit) async {
emit(state.copyWith(
actionStatus: AiConfigActionStatus.loading,
actionErrorMessage: () => null));
try {
AppLogger.i('AiConfigBloc', '开始添加自定义模型并验证: provider=${event.provider}, modelName=${event.modelName}');
// 首先添加配置
final newConfig = await _repository.addConfiguration(
userId: event.userId,
provider: event.provider,
modelName: event.modelName,
alias: event.alias,
apiKey: event.apiKey,
apiEndpoint: event.apiEndpoint,
);
AppLogger.i('AiConfigBloc', '自定义模型添加成功: configId=${newConfig.id}');
// 立即验证配置
try {
final validatedConfig = await _repository.validateConfiguration(
userId: event.userId,
configId: newConfig.id,
);
AppLogger.i('AiConfigBloc', '自定义模型验证完成: configId=${newConfig.id}, isValidated=${validatedConfig.isValidated}');
// 直接更新列表,避免重复请求
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
currentConfigs.add(validatedConfig);
// 重新构建providerDefaultConfigs
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
// 使缓存失效,确保下次加载最新数据
_lastConfigsLoadTime = null;
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs,
));
AppLogger.i('AiConfigBloc', '自定义模型添加和验证完成,列表已更新');
} catch (validateError) {
AppLogger.w('AiConfigBloc', '自定义模型验证失败,但配置已添加: ${validateError.toString()}');
// 验证失败,但配置已添加,仍然更新列表
final currentConfigs = List<UserAIModelConfigModel>.from(state.configs);
currentConfigs.add(newConfig);
final providerDefaultConfigs = _buildProviderDefaultConfigs(currentConfigs);
_lastConfigsLoadTime = null;
emit(state.copyWith(
actionStatus: AiConfigActionStatus.success,
configs: currentConfigs,
providerDefaultConfigs: providerDefaultConfigs,
));
}
} catch (e, stackTrace) {
AppLogger.e('AiConfigBloc', '添加自定义模型失败', e, stackTrace);
emit(state.copyWith(
actionStatus: AiConfigActionStatus.error,
actionErrorMessage: () => '添加自定义模型失败: ${e.toString()}'));
}
}
void _onClearModelsCache(ClearModelsCache event, Emitter<AiConfigState> emit) {
if (event.provider != null) {
// 清除特定提供商的缓存
_modelsCacheTime.remove(event.provider);
AppLogger.i('AiConfigBloc', '已清除提供商 ${event.provider} 的模型缓存');
} else {
// 清除所有模型缓存
_modelsCacheTime.clear();
AppLogger.i('AiConfigBloc', '已清除所有模型缓存');
}
}
}