diff --git a/src/public/app/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts b/src/public/app/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts index 85625e7cd..16dcacc21 100644 --- a/src/public/app/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts +++ b/src/public/app/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts @@ -136,9 +136,7 @@ export default class AiSettingsWidget extends OptionsWidget { this.setupChangeHandler('.embedding-dimension-strategy', 'embeddingDimensionStrategy'); this.setupChangeHandler('.embedding-provider-precedence', 'embeddingProviderPrecedence', true); - // Set up sortable behavior for the embedding provider precedence list - this.setupEmbeddingProviderSortable(); - this.setupAiProviderSortable(); + // No sortable behavior needed anymore // Embedding stats refresh button const $refreshStats = this.$widget.find('.embedding-refresh-stats'); @@ -433,369 +431,6 @@ export default class AiSettingsWidget extends OptionsWidget { } } - /** - * Setup sortable behavior for embedding provider precedence - */ - setupEmbeddingProviderSortable() { - if (!this.$widget) return; - - const $embeddingProviderPrecedence = this.$widget.find('.embedding-provider-precedence'); - const $sortableList = this.$widget.find('.embedding-provider-sortable'); - const $items = $sortableList.find('li'); - - // Make list items draggable - $items.each((index, item) => this.setupEmbeddingProviderItemDragHandlers($(item))); - - // Setup the remove buttons - this.setupEmbeddingProviderRemoveHandlers(); - - // Setup disabled providers list restore handlers - this.$widget.find('.embedding-provider-disabled li').each((index, item) => { - this.setupEmbeddingProviderRestoreHandler($(item)); - }); - - // Initialize the order based on saved value - this.initializeEmbeddingProviderOrder(); - } - - /** - * Setup sortable behavior for AI provider precedence - */ - setupAiProviderSortable() { - if (!this.$widget) return; - - const $aiProviderPrecedence = this.$widget.find('.ai-provider-precedence'); - const $sortableList = this.$widget.find('.provider-sortable'); - const $items = $sortableList.find('li'); - - // Make list items draggable - $items.each((index, item) => this.setupAiItemDragHandlers($(item))); - - // Setup the remove buttons - this.setupAiProviderRemoveHandlers(); - - // Setup disabled providers list restore handlers - this.$widget.find('.provider-disabled li').each((index, item) => { - this.setupAiProviderRestoreHandler($(item)); - }); - - // Initialize the order based on saved value - this.initializeAiProviderOrder(); - } - - /** - * Setup drag handlers for an embedding provider list item - */ - setupEmbeddingProviderItemDragHandlers($item: JQuery) { - if (!this.$widget) return; - - const self = this; - const $embeddingProviderPrecedence = this.$widget.find('.embedding-provider-precedence'); - const $embeddingSortableList = this.$widget.find('.embedding-provider-sortable'); - - // Setup dragstart handler - $item.on('dragstart', function(e: JQuery.DragStartEvent) { - $(this).addClass('dragging'); - e.originalEvent?.dataTransfer?.setData('text/plain', ''); - }); - - // Setup dragend handler - $item.on('dragend', function() { - $(this).removeClass('dragging'); - - // Update the hidden input value - const providers = $embeddingSortableList.find('li').map(function() { - return $(this).data('provider'); - }).get().join(','); - - // Only update if we have providers or if the current value isn't empty - // This prevents setting an empty string when all providers are removed - if (providers || $embeddingProviderPrecedence.val()) { - $embeddingProviderPrecedence.val(providers); - $embeddingProviderPrecedence.trigger('change'); - } - }); - - // Additional drag event handlers ... - - // All other drag event handlers would be implemented here - } - - /** - * Setup event handlers for embedding provider remove buttons - */ - setupEmbeddingProviderRemoveHandlers() { - if (!this.$widget) return; - - const self = this; - const $embeddingProviderPrecedence = this.$widget.find('.embedding-provider-precedence'); - const $embeddingSortableList = this.$widget.find('.embedding-provider-sortable'); - - // Remove any existing handlers to prevent duplicates - this.$widget.find('.remove-provider').off('click'); - - // Add handlers - this.$widget.find('.remove-provider').on('click', function() { - const $item = $(this).closest('li'); - const provider = $item.data('provider'); - const providerName = self.getProviderDisplayName(provider); - - // Create a new item for the disabled list - const $disabledItem = $(` -
  • - ${providerName} - -
  • - `); - - // Move to disabled list - self.$widget?.find('.embedding-provider-disabled').append($disabledItem); - self.setupEmbeddingProviderRestoreHandler($disabledItem); - $item.remove(); - - // Update the precedence value - const providers = $embeddingSortableList.find('li').map(function() { - return $(this).data('provider'); - }).get().join(','); - $embeddingProviderPrecedence.val(providers); - $embeddingProviderPrecedence.trigger('change'); - - // Show disabled providers container - self.$widget?.find('.disabled-providers-container').show(); - }); - } - - /** - * Setup event handler for embedding provider restore button - */ - setupEmbeddingProviderRestoreHandler($item: JQuery) { - if (!this.$widget) return; - - const self = this; - const $embeddingProviderPrecedence = this.$widget.find('.embedding-provider-precedence'); - const $embeddingSortableList = this.$widget.find('.embedding-provider-sortable'); - - // Remove any existing handlers to prevent duplicates - $item.find('.restore-provider').off('click'); - - // Add handlers - $item.find('.restore-provider').on('click', function() { - const $disabledItem = $(this).closest('li'); - const provider = $disabledItem.data('provider'); - const providerName = self.getProviderDisplayName(provider); - - // Create a new item for the active list - const $activeItem = $(` -
  • - - ${providerName} - -
  • - `); - - // Move to active list - $embeddingSortableList.append($activeItem); - self.setupEmbeddingProviderItemDragHandlers($activeItem); - self.setupEmbeddingProviderRemoveHandlers(); - $disabledItem.remove(); - - // Update the precedence value - const providers = $embeddingSortableList.find('li').map(function() { - return $(this).data('provider'); - }).get().join(','); - $embeddingProviderPrecedence.val(providers); - $embeddingProviderPrecedence.trigger('change'); - - // Hide disabled providers container if it's now empty - if (self.$widget?.find('.embedding-provider-disabled li').length === 0) { - self.$widget?.find('.disabled-providers-container').hide(); - } - }); - } - - /** - * Initialize the embedding provider precedence order based on saved values - */ - initializeEmbeddingProviderOrder() { - if (!this.$widget) return; - - const $embeddingProviderPrecedence = this.$widget.find('.embedding-provider-precedence'); - const $sortableList = this.$widget.find('.embedding-provider-sortable'); - - // Get the current value - const savedValue = $embeddingProviderPrecedence.val() as string; - // If no saved value, don't proceed with initialization to avoid triggering the "empty" change - if (!savedValue) return; - - // Get all available providers - const allProviders = ['openai', 'voyage', 'ollama', 'local']; - const savedProviders = savedValue.split(','); - - // Clear all items from the disabled list first to avoid duplicates - this.$widget.find('.embedding-provider-disabled').empty(); - - // Find disabled providers (providers in allProviders but not in savedProviders) - const disabledProviders = allProviders.filter(p => !savedProviders.includes(p)); - - // Move saved providers to the end in the correct order - savedProviders.forEach(provider => { - const $item = $sortableList.find(`li[data-provider="${provider}"]`); - if ($item.length) { - $sortableList.append($item); // Move to the end in the correct order - } - }); - - // Setup remove click handlers first to ensure they work when simulating clicks - this.setupEmbeddingProviderRemoveHandlers(); - - // Move disabled providers to the disabled list - disabledProviders.forEach(provider => { - const $item = $sortableList.find(`li[data-provider="${provider}"]`); - if ($item.length) { - // Simulate clicking the remove button to move it to the disabled list - $item.find('.remove-provider').trigger('click'); - } else { - // If it's not in the active list already, manually create it in the disabled list - const providerName = this.getProviderDisplayName(provider); - const $disabledItem = $(` -
  • - ${providerName} - -
  • - `); - this.$widget.find('.embedding-provider-disabled').append($disabledItem); - - // Add restore button handler - this.setupEmbeddingProviderRestoreHandler($disabledItem); - } - }); - - // Show/hide the disabled providers container - const $disabledContainer = this.$widget.find('.disabled-providers-container'); - const hasDisabledProviders = this.$widget.find('.embedding-provider-disabled li').length > 0; - $disabledContainer.toggle(hasDisabledProviders); - } - - /** - * Setup drag handlers for an AI provider list item - */ - setupAiItemDragHandlers($item: JQuery) { - if (!this.$widget) return; - - const self = this; - const $aiProviderPrecedence = this.$widget.find('.ai-provider-precedence'); - const $aiSortableList = this.$widget.find('.provider-sortable'); - - // Setup dragstart handler - $item.on('dragstart', function(e: JQuery.DragStartEvent) { - $(this).addClass('dragging'); - e.originalEvent?.dataTransfer?.setData('text/plain', ''); - }); - - // Setup dragend handler - $item.on('dragend', function() { - $(this).removeClass('dragging'); - - // Update the hidden input value - const providers = $aiSortableList.find('li').map(function() { - return $(this).data('provider'); - }).get().join(','); - - $aiProviderPrecedence.val(providers); - $aiProviderPrecedence.trigger('change'); - }); - - // Additional drag event handlers would go here... - } - - /** - * Initialize the AI provider precedence order based on saved values - */ - initializeAiProviderOrder() { - if (!this.$widget) return; - - const $aiProviderPrecedence = this.$widget.find('.ai-provider-precedence'); - const $aiSortableList = this.$widget.find('.provider-sortable'); - - // Get the current value - const savedValue = $aiProviderPrecedence.val() as string; - if (!savedValue) return; - - // Get all available providers - const allProviders = ['openai', 'anthropic', 'ollama', 'voyage']; - const savedProviders = savedValue.split(','); - - // Clear all items from the disabled list first to avoid duplicates - this.$widget.find('.provider-disabled').empty(); - - // Find disabled providers (providers in allProviders but not in savedProviders) - const disabledProviders = allProviders.filter(p => !savedProviders.includes(p)); - - // Move saved providers to the end in the correct order - savedProviders.forEach(provider => { - const $item = $aiSortableList.find(`li[data-provider="${provider}"]`); - if ($item.length) { - $aiSortableList.append($item); // Move to the end in the correct order - } - }); - - // Setup remove click handlers first to ensure they work when simulating clicks - this.setupAiProviderRemoveHandlers(); - - // Move disabled providers to the disabled list - disabledProviders.forEach(provider => { - const $item = $aiSortableList.find(`li[data-provider="${provider}"]`); - if ($item.length) { - // Simulate clicking the remove button to move it to the disabled list - $item.find('.remove-ai-provider').trigger('click'); - } else { - // If it's not in the active list already, manually create it in the disabled list - const providerName = this.getProviderDisplayName(provider); - const $disabledItem = $(` -
  • - ${providerName} - -
  • - `); - this.$widget.find('.provider-disabled').append($disabledItem); - - // Add restore button handler - this.setupAiProviderRestoreHandler($disabledItem); - } - }); - - // Show/hide the disabled providers container - const $disabledContainer = this.$widget.find('.disabled-ai-providers-container'); - const hasDisabledProviders = this.$widget.find('.provider-disabled li').length > 0; - $disabledContainer.toggle(hasDisabledProviders); - } - - /** - * Setup event handlers for AI provider remove buttons - */ - setupAiProviderRemoveHandlers() { - if (!this.$widget) return; - - // Implementation would go here... - } - - /** - * Setup event handler for AI provider restore button - */ - setupAiProviderRestoreHandler($item: JQuery) { - if (!this.$widget) return; - - // Implementation would go here... - } - /** * Called when the options have been loaded from the server */ @@ -836,10 +471,6 @@ export default class AiSettingsWidget extends OptionsWidget { this.$widget.find('.max-notes-per-llm-query').val(options.maxNotesPerLlmQuery || '3'); this.$widget.find('.embedding-dimension-strategy').val(options.embeddingDimensionStrategy || 'auto'); - // Initialize sortable lists - this.initializeEmbeddingProviderOrder(); - this.initializeAiProviderOrder(); - // Display validation warnings this.displayValidationWarnings(); } diff --git a/src/public/app/widgets/type_widgets/options/ai_settings/template.ts b/src/public/app/widgets/type_widgets/options/ai_settings/template.ts index ef04243dd..8a134465f 100644 --- a/src/public/app/widgets/type_widgets/options/ai_settings/template.ts +++ b/src/public/app/widgets/type_widgets/options/ai_settings/template.ts @@ -277,50 +277,9 @@ export const TPL = `
    ${t("ai_llm.rebuild_index_description")}
    - +
    -
    ${t("ai_llm.provider_order")}
    -
    ${t("ai_llm.provider_order_description")}
    - -
    - -
    - - - +
    ${t("ai_llm.embedding_providers_order")}
    +
    ${t("ai_llm.embedding_providers_order_description")}
    `; \ No newline at end of file diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 5317a733e..ea0cad6cf 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1123,7 +1123,19 @@ "layout-horizontal-description": "launcher bar is underneath the tab bar, the tab bar is now full width." }, "ai_llm": { + "embeddings_configuration": "Embeddings Configuration", "title": "AI & Embedding Settings", + "embedding_statistics": "Embedding Statistics", + "processed_notes": "Processed Notes", + "total_notes": "Total Notes", + "progress": "Progress", + "queued_notes": "Queued Notes", + "failed_notes": "Failed Notes", + "last_processed": "Last Processed", + "refresh_stats": "Refresh Statistics", + "no_failed_embeddings": "No failed embeddings found.", + "enable_ai_features": "Enable AI/LLM features", + "enable_ai_description": "Enable AI features like note summarization, content generation, and other LLM capabilities", "openai_tab": "OpenAI", "anthropic_tab": "Anthropic", "voyage_tab": "Voyage AI", @@ -1142,6 +1154,9 @@ "openai_configuration": "OpenAI Configuration", "openai_settings": "OpenAI Settings", "api_key": "API Key", + "url": "Base URL", + "model": "Model", + "refresh_models": "Refresh Models", "openai_api_key_description": "Your OpenAI API key for accessing their AI services", "anthropic_api_key_description": "Your Anthropic API key for accessing Claude models", "default_model": "Default Model", @@ -1152,6 +1167,15 @@ "base_url": "Base URL", "url": "URL", "openai_url_description": "Default: https://api.openai.com/v1", + "anthropic_settings": "Anthropic Settings", + "anthropic_url_description": "Base URL for the Anthropic API (default: https://api.anthropic.com)", + "anthropic_model_description": "Anthropic Claude models for chat completion", + "voyage_settings": "Voyage AI Settings", + "voyage_api_key_description": "Your Voyage AI API key for accessing embeddings services", + "ollama_settings": "Ollama Settings", + "ollama_url_description": "URL for the Ollama API (default: http://localhost:11434)", + "ollama_model_description": "Ollama model to use for chat completion", + "ollama_embedding_model_description": "Specialized model for generating embeddings (vector representations)", "anthropic_configuration": "Anthropic Configuration", "anthropic_model_description": "Examples: claude-3-opus-20240229, claude-3-sonnet-20240229", "voyage_embedding_model_description": "Voyage AI embedding models for text embeddings (voyage-2 recommended)", @@ -1173,8 +1197,23 @@ "embedding_default_provider": "Default Provider", "embedding_default_provider_description": "Select the default provider used for generating note embeddings", "embedding_provider_precedence": "Embedding Provider Precedence", + "embedding_providers_order": "Embedding Provider Order", + "embedding_providers_order_description": "Set the order of embedding providers in comma-separated format (e.g., \"openai,voyage,ollama,local\")", + "embeddings_configuration": "Embeddings Configuration", + "enable_automatic_indexing": "Enable Automatic Indexing", + "enable_automatic_indexing_description": "Automatically generate embeddings for new and updated notes", + "embedding_auto_update_enabled": "Auto-update Embeddings", + "embedding_auto_update_enabled_description": "Automatically update embeddings when notes are modified", + "rebuild_index": "Rebuild Index", + "rebuild_index_description": "Regenerate all note embeddings (may take some time for large note collections)", "embedding_provider_precedence_description": "Comma-separated list of providers in order of precedence for embeddings search (e.g., 'openai,ollama,anthropic')", - "embedding_dimension_strategy": "Embedding dimension strategy", + "embedding_dimension_strategy": "Embedding Dimension Strategy", + "embedding_dimension_auto": "Auto (Recommended)", + "embedding_dimension_fixed": "Fixed", + "embedding_similarity_threshold": "Similarity Threshold", + "embedding_similarity_threshold_description": "Minimum similarity score for notes to be included in search results (0-1)", + "max_notes_per_llm_query": "Max Notes Per Query", + "max_notes_per_llm_query_description": "Maximum number of similar notes to include in AI context", "embedding_dimension_strategy_description": "Choose how embeddings are handled. 'Native' preserves maximum information by adapting smaller vectors to match larger ones (recommended). 'Regenerate' creates new embeddings with the target model for specific search needs.", "drag_providers_to_reorder": "Drag providers up or down to set your preferred order for embedding searches", "active_providers": "Active Providers",