Files
MaliangAINovalWriter/AINoval/lib/blocs/ai_config/ai_config_bloc.dart
2025-09-10 00:07:52 +08:00

747 lines
30 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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', '已清除所有模型缓存');
}
}
}