diff --git a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts index ffe4a114c..5b2480c1a 100644 --- a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts +++ b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts @@ -83,11 +83,17 @@ export default class AiSettingsWidget extends OptionsWidget { // Voyage options this.setupChangeHandler('.voyage-api-key', 'voyageApiKey'); this.setupChangeHandler('.voyage-embedding-model', 'voyageEmbeddingModel'); + this.setupChangeHandler('.voyage-embedding-base-url', 'voyageEmbeddingBaseUrl'); // Ollama options this.setupChangeHandler('.ollama-base-url', 'ollamaBaseUrl'); this.setupChangeHandler('.ollama-default-model', 'ollamaDefaultModel'); this.setupChangeHandler('.ollama-embedding-model', 'ollamaEmbeddingModel'); + this.setupChangeHandler('.ollama-embedding-base-url', 'ollamaEmbeddingBaseUrl'); + + // Embedding-specific provider options + this.setupChangeHandler('.openai-embedding-api-key', 'openaiEmbeddingApiKey', true); + this.setupChangeHandler('.openai-embedding-base-url', 'openaiEmbeddingBaseUrl', true); const $refreshModels = this.$widget.find('.refresh-models'); $refreshModels.on('click', async () => { @@ -215,6 +221,37 @@ export default class AiSettingsWidget extends OptionsWidget { } }); + // Add embedding base URL change handlers to trigger model fetching + this.$widget.find('.openai-embedding-base-url').on('change', async () => { + const selectedEmbeddingProvider = this.$widget.find('.embedding-selected-provider').val() as string; + if (selectedEmbeddingProvider === 'openai') { + await this.fetchModelsForProvider('openai', 'embedding'); + } + }); + + this.$widget.find('.voyage-embedding-base-url').on('change', async () => { + const selectedEmbeddingProvider = this.$widget.find('.embedding-selected-provider').val() as string; + if (selectedEmbeddingProvider === 'voyage') { + // Voyage doesn't have dynamic model fetching yet, but we can add it here when implemented + console.log('Voyage embedding base URL changed - model fetching not yet implemented'); + } + }); + + this.$widget.find('.ollama-embedding-base-url').on('change', async () => { + const selectedEmbeddingProvider = this.$widget.find('.embedding-selected-provider').val() as string; + if (selectedEmbeddingProvider === 'ollama') { + await this.fetchModelsForProvider('ollama', 'embedding'); + } + }); + + // Add embedding API key change handlers to trigger model fetching + this.$widget.find('.openai-embedding-api-key').on('change', async () => { + const selectedEmbeddingProvider = this.$widget.find('.embedding-selected-provider').val() as string; + if (selectedEmbeddingProvider === 'openai') { + await this.fetchModelsForProvider('openai', 'embedding'); + } + }); + // No sortable behavior needed anymore // Embedding stats refresh button @@ -514,13 +551,13 @@ export default class AiSettingsWidget extends OptionsWidget { if (!this.$widget || !value) return; const $dropdown = this.$widget.find(selector); - + // Check if the value already exists as an option if ($dropdown.find(`option[value="${value}"]`).length === 0) { // Add the custom value as an option $dropdown.append(``); } - + // Set the value $dropdown.val(value); } @@ -596,13 +633,19 @@ export default class AiSettingsWidget extends OptionsWidget { // Voyage Section this.$widget.find('.voyage-api-key').val(options.voyageApiKey || ''); + this.$widget.find('.voyage-embedding-base-url').val(options.voyageEmbeddingBaseUrl || 'https://api.voyageai.com/v1'); this.setModelDropdownValue('.voyage-embedding-model', options.voyageEmbeddingModel); // Ollama Section this.$widget.find('.ollama-base-url').val(options.ollamaBaseUrl || 'http://localhost:11434'); + this.$widget.find('.ollama-embedding-base-url').val(options.ollamaEmbeddingBaseUrl || 'http://localhost:11434'); this.setModelDropdownValue('.ollama-default-model', options.ollamaDefaultModel); this.setModelDropdownValue('.ollama-embedding-model', options.ollamaEmbeddingModel); + // Embedding-specific provider options + this.$widget.find('.openai-embedding-api-key').val(options.openaiEmbeddingApiKey || ''); + this.$widget.find('.openai-embedding-base-url').val(options.openaiEmbeddingBaseUrl || 'https://api.openai.com/v1'); + // Embedding Options this.$widget.find('.embedding-selected-provider').val(options.embeddingSelectedProvider || 'openai'); this.$widget.find('.embedding-auto-update-enabled').prop('checked', options.embeddingAutoUpdateEnabled !== 'false'); @@ -619,11 +662,11 @@ export default class AiSettingsWidget extends OptionsWidget { // Automatically fetch models for currently selected providers const selectedAiProvider = this.$widget.find('.ai-selected-provider').val() as string; const selectedEmbeddingProvider = this.$widget.find('.embedding-selected-provider').val() as string; - + if (selectedAiProvider) { await this.fetchModelsForProvider(selectedAiProvider, 'chat'); } - + if (selectedEmbeddingProvider) { await this.fetchModelsForProvider(selectedEmbeddingProvider, 'embedding'); } diff --git a/apps/client/src/widgets/type_widgets/options/ai_settings/template.ts b/apps/client/src/widgets/type_widgets/options/ai_settings/template.ts index 5115c2144..4778930af 100644 --- a/apps/client/src/widgets/type_widgets/options/ai_settings/template.ts +++ b/apps/client/src/widgets/type_widgets/options/ai_settings/template.ts @@ -210,6 +210,18 @@ export const TPL = `
${t("ai_llm.openai_embedding_settings")}
+
+ + +
${t("ai_llm.openai_embedding_api_key_description")}
+
+ +
+ + +
${t("ai_llm.openai_embedding_url_description")}
+
+
${t("ai_llm.openai_embedding_model_description")}
-
${t("ai_llm.openai_embedding_shared_settings")}
@@ -235,6 +246,12 @@ export const TPL = `
${t("ai_llm.voyage_api_key_description")}
+
+ + +
${t("ai_llm.voyage_embedding_url_description")}
+
+
+
${t("ai_llm.ollama_embedding_url_description")}
+
+
${t("ai_llm.ollama_embedding_model_description")}
-
${t("ai_llm.ollama_embedding_shared_settings")}
diff --git a/apps/server/src/services/options_init.ts b/apps/server/src/services/options_init.ts index 817d91fba..5311a67f0 100644 --- a/apps/server/src/services/options_init.ts +++ b/apps/server/src/services/options_init.ts @@ -209,6 +209,12 @@ const defaultOptions: DefaultOption[] = [ { name: "ollamaEmbeddingModel", value: "", isSynced: true }, { name: "embeddingAutoUpdateEnabled", value: "true", isSynced: true }, + // Embedding-specific provider options + { name: "openaiEmbeddingApiKey", value: "", isSynced: false }, + { name: "openaiEmbeddingBaseUrl", value: "https://api.openai.com/v1", isSynced: true }, + { name: "voyageEmbeddingBaseUrl", value: "https://api.voyageai.com/v1", isSynced: true }, + { name: "ollamaEmbeddingBaseUrl", value: "http://localhost:11434", isSynced: true }, + // Adding missing AI options { name: "aiTemperature", value: "0.7", isSynced: true }, { name: "aiSystemPrompt", value: "", isSynced: true }, diff --git a/packages/commons/src/lib/options_interface.ts b/packages/commons/src/lib/options_interface.ts index e53d1d9c1..45faf4d79 100644 --- a/packages/commons/src/lib/options_interface.ts +++ b/packages/commons/src/lib/options_interface.ts @@ -131,16 +131,20 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions