refactor: Improve model tools auto-selection logic
This commit refactors the `modelToolsAutoSelectionProvider` to more robustly handle the automatic selection of tools when the model changes or the list of available tools is updated. Key changes include: - Triggering the auto-selection logic not only when the selected model changes, but also when the `toolsListProvider` finishes loading. This ensures tools are correctly applied even if they load after the model is selected. - Restructuring the logic to handle asynchronous tool loading more gracefully, preventing race conditions. - Using `Future.microtask` to schedule the tool application, ensuring state updates are handled correctly within the provider lifecycle. - Clearing selected tools if the new model has no associated tools or if previously selected tools become invalid.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
@@ -17,6 +18,7 @@ import '../models/chat_message.dart';
|
|||||||
import '../models/folder.dart';
|
import '../models/folder.dart';
|
||||||
import '../models/user_settings.dart';
|
import '../models/user_settings.dart';
|
||||||
import '../models/file_info.dart';
|
import '../models/file_info.dart';
|
||||||
|
import '../models/tool.dart';
|
||||||
import '../models/knowledge_base.dart';
|
import '../models/knowledge_base.dart';
|
||||||
import '../services/settings_service.dart';
|
import '../services/settings_service.dart';
|
||||||
import '../services/optimized_storage_service.dart';
|
import '../services/optimized_storage_service.dart';
|
||||||
@@ -646,36 +648,84 @@ final _settingsWatcherProvider = Provider<void>((ref) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-apply model-specific tools when model changes
|
// Auto-apply model-specific tools when model changes or tools load
|
||||||
final modelToolsAutoSelectionProvider = Provider<void>((ref) {
|
final modelToolsAutoSelectionProvider = Provider<void>((ref) {
|
||||||
ref.listen<Model?>(selectedModelProvider, (previous, next) {
|
Future<void> applyTools(Model? model) async {
|
||||||
// Only react when the model actually changes
|
if (model == null) {
|
||||||
if (previous?.id == next?.id) return;
|
final current = ref.read(selectedToolIdsProvider);
|
||||||
if (next == null) return;
|
if (current.isNotEmpty) {
|
||||||
|
ref.read(selectedToolIdsProvider.notifier).set([]);
|
||||||
// Load tools configured for this model
|
}
|
||||||
final modelToolIds = next.toolIds ?? [];
|
return;
|
||||||
if (modelToolIds.isNotEmpty) {
|
|
||||||
// Filter to only include tools that are actually available
|
|
||||||
final toolsAsync = ref.read(toolsListProvider);
|
|
||||||
toolsAsync.whenData((availableTools) {
|
|
||||||
final validToolIds = modelToolIds
|
|
||||||
.where((id) => availableTools.any((t) => t.id == id))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (validToolIds.isNotEmpty) {
|
|
||||||
ref.read(selectedToolIdsProvider.notifier).set(validToolIds);
|
|
||||||
DebugLogger.log(
|
|
||||||
'auto-apply-tools',
|
|
||||||
scope: 'models/tools',
|
|
||||||
data: {'modelId': next.id, 'toolCount': validToolIds.length},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Clear tools if model has no configured tools
|
|
||||||
ref.read(selectedToolIdsProvider.notifier).set([]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final modelToolIds = model.toolIds ?? [];
|
||||||
|
if (modelToolIds.isEmpty) {
|
||||||
|
final current = ref.read(selectedToolIdsProvider);
|
||||||
|
if (current.isNotEmpty) {
|
||||||
|
ref.read(selectedToolIdsProvider.notifier).set([]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSelection(List<Tool> availableTools) {
|
||||||
|
final validToolIds = modelToolIds
|
||||||
|
.where((id) => availableTools.any((tool) => tool.id == id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final currentSelection = ref.read(selectedToolIdsProvider);
|
||||||
|
if (validToolIds.isEmpty) {
|
||||||
|
if (currentSelection.isNotEmpty) {
|
||||||
|
ref.read(selectedToolIdsProvider.notifier).set([]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (listEquals(currentSelection, validToolIds)) return;
|
||||||
|
|
||||||
|
ref.read(selectedToolIdsProvider.notifier).set(validToolIds);
|
||||||
|
DebugLogger.log(
|
||||||
|
'auto-apply-tools',
|
||||||
|
scope: 'models/tools',
|
||||||
|
data: {'modelId': model.id, 'toolCount': validToolIds.length},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final toolsAsync = ref.read(toolsListProvider);
|
||||||
|
if (toolsAsync.hasValue) {
|
||||||
|
updateSelection(toolsAsync.value ?? const <Tool>[]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final availableTools = await ref.read(toolsListProvider.future);
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
updateSelection(availableTools);
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
DebugLogger.error(
|
||||||
|
'auto-apply-tools-failed',
|
||||||
|
scope: 'models/tools',
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> scheduleApply(Model? model) async {
|
||||||
|
await applyTools(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future.microtask(() => scheduleApply(ref.read(selectedModelProvider)));
|
||||||
|
|
||||||
|
ref.listen<Model?>(selectedModelProvider, (previous, next) {
|
||||||
|
if (previous?.id == next?.id && previous != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Future.microtask(() => scheduleApply(next));
|
||||||
|
});
|
||||||
|
|
||||||
|
ref.listen(toolsListProvider, (previous, next) {
|
||||||
|
if (!next.hasValue) return;
|
||||||
|
Future.microtask(() => scheduleApply(ref.read(selectedModelProvider)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user