diff --git a/lib/services/ollama_service.dart b/lib/services/ollama_service.dart index 3930185..60ad3b7 100644 --- a/lib/services/ollama_service.dart +++ b/lib/services/ollama_service.dart @@ -118,6 +118,27 @@ class OllamaService implements LlmService { return await _secureStorage.read(key: _modelKey) ?? _defaultModel; } + Future> getAvailableEmbeddingModels() async { + final endpoint = + await _secureStorage.read(key: _endpointKey) ?? _defaultEndpoint; + + try { + final response = await _dio.get('$endpoint/api/tags'); + + if (response.statusCode == 200) { + final models = (response.data['models'] as List) + .map((model) => model['name'] as String) + .where((name) => name.contains('embed')) + .toList(); + return models.isEmpty ? [_defaultEmbeddingModel] : models; + } else { + return [_defaultEmbeddingModel]; + } + } catch (e) { + return [_defaultEmbeddingModel]; + } + } + Future setEmbeddingModel(String model) async { await _secureStorage.write(key: _embeddingModelKey, value: model); } diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart index 65c2b4f..f81c73d 100644 --- a/lib/ui/screens/settings_screen.dart +++ b/lib/ui/screens/settings_screen.dart @@ -435,9 +435,10 @@ class OllamaSettings extends ConsumerStatefulWidget { class _OllamaSettingsState extends ConsumerState { final TextEditingController _endpointController = TextEditingController(); - final TextEditingController _embeddingModelController = TextEditingController(); String? _selectedModel; + String? _selectedEmbeddingModel; List _availableModels = []; + List _availableEmbeddingModels = []; bool _isLoading = false; @override @@ -449,7 +450,6 @@ class _OllamaSettingsState extends ConsumerState { @override void dispose() { _endpointController.dispose(); - _embeddingModelController.dispose(); super.dispose(); } @@ -470,18 +470,26 @@ class _OllamaSettingsState extends ConsumerState { // Load embedding model final embeddingModel = await ollamaService.getCurrentEmbeddingModel(); - _embeddingModelController.text = embeddingModel; // Load available models final models = await ollamaService.getAvailableModels(); + // Load available embedding models + final embeddingModels = + await ollamaService.getAvailableEmbeddingModels(); setState(() { _availableModels = models; + _availableEmbeddingModels = embeddingModels; if (models.contains(currentModel)) { _selectedModel = currentModel; } else { _selectedModel = null; } + if (embeddingModels.contains(embeddingModel)) { + _selectedEmbeddingModel = embeddingModel; + } else { + _selectedEmbeddingModel = null; + } }); } catch (e) { // Handle error @@ -510,9 +518,9 @@ class _OllamaSettingsState extends ConsumerState { await ollamaService.setModel(_selectedModel!); } - await ollamaService.setEmbeddingModel( - _embeddingModelController.text.trim(), - ); + if (_selectedEmbeddingModel != null) { + await ollamaService.setEmbeddingModel(_selectedEmbeddingModel!); + } await _loadSettings(); @@ -567,12 +575,23 @@ class _OllamaSettingsState extends ConsumerState { }, ), const SizedBox(height: 16), - TextField( - controller: _embeddingModelController, + DropdownButtonFormField( decoration: const InputDecoration( labelText: 'Embedding Model', border: OutlineInputBorder(), ), + value: _selectedEmbeddingModel, + items: _availableEmbeddingModels.map((model) { + return DropdownMenuItem( + value: model, + child: Text(model), + ); + }).toList(), + onChanged: (value) { + setState(() { + _selectedEmbeddingModel = value; + }); + }, ), const SizedBox(height: 16), ElevatedButton(