Vectors WebLLM (#3631) * Add WebLLM support for vectorization * Load models when WebLLM extension installed * Consistency updated * Move checkWebLlm to initEngine * Refactor vector request handling to use getAdditionalArgs * Add error handling for unsupported WebLLM extension * Add prefix to error causes
Signed| @@ -1070,7 +1070,7 @@ export async function installExtension(url, global) { | |||
| 1070 | toastr.success(t`Extension '${response.display_name}' by ${response.author} (version ${response.version}) has been installed successfully!`, t`Extension installation successful`); | 1070 | toastr.success(t`Extension '${response.display_name}' by ${response.author} (version ${response.version}) has been installed successfully!`, t`Extension installation successful`); |
| 1071 | console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`); | 1071 | console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`); |
| 1072 | await loadExtensionSettings({}, false, false); | 1072 | await loadExtensionSettings({}, false, false); |
| 1073 | await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED); | 1073 | await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED, response); |
| 1074 | } | 1074 | } |
| 1075 | 1075 | ||
| 1076 | /** | 1076 | /** |
| @@ -19,6 +19,7 @@ import { | |||
| 19 | modules, | 19 | modules, |
| 20 | renderExtensionTemplateAsync, | 20 | renderExtensionTemplateAsync, |
| 21 | doExtrasFetch, getApiUrl, | 21 | doExtrasFetch, getApiUrl, |
| 22 | openThirdPartyExtensionMenu, | ||
| 22 | } from '../../extensions.js'; | 23 | } from '../../extensions.js'; |
| 23 | import { collapseNewlines, registerDebugFunction } from '../../power-user.js'; | 24 | import { collapseNewlines, registerDebugFunction } from '../../power-user.js'; |
| 24 | import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; | 25 | import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; |
| @@ -34,6 +35,7 @@ import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashComm | |||
| 34 | import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; | 35 | import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; |
| 35 | import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js'; | 36 | import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js'; |
| 36 | import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js'; | 37 | import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js'; |
| 38 | import { WebLlmVectorProvider } from './webllm.js'; | ||
| 37 | 39 | ||
| 38 | /** | 40 | /** |
| 39 | * @typedef {object} HashedMessage | 41 | * @typedef {object} HashedMessage |
| @@ -60,6 +62,7 @@ const settings = { | |||
| 60 | ollama_model: 'mxbai-embed-large', | 62 | ollama_model: 'mxbai-embed-large', |
| 61 | ollama_keep: false, | 63 | ollama_keep: false, |
| 62 | vllm_model: '', | 64 | vllm_model: '', |
| 65 | webllm_model: '', | ||
| 63 | summarize: false, | 66 | summarize: false, |
| 64 | summarize_sent: false, | 67 | summarize_sent: false, |
| 65 | summary_source: 'main', | 68 | summary_source: 'main', |
| @@ -103,7 +106,7 @@ const settings = { | |||
| 103 | }; | 106 | }; |
| 104 | 107 | ||
| 105 | const moduleWorker = new ModuleWorkerWrapper(synchronizeChat); | 108 | const moduleWorker = new ModuleWorkerWrapper(synchronizeChat); |
| 106 | 109 | const webllmProvider = new WebLlmVectorProvider(); | |
| 107 | const cachedSummaries = new Map(); | 110 | const cachedSummaries = new Map(); |
| 108 | 111 | ||
| 109 | /** | 112 | /** |
| @@ -373,6 +376,8 @@ async function synchronizeChat(batchSize = 5) { | |||
| 373 | return 'Vectorization Source Model is required, but not set.'; | 376 | return 'Vectorization Source Model is required, but not set.'; |
| 374 | case 'extras_module_missing': | 377 | case 'extras_module_missing': |
| 375 | return 'Extras API must provide an "embeddings" module.'; | 378 | return 'Extras API must provide an "embeddings" module.'; |
| 379 | case 'webllm_not_supported': | ||
| 380 | return 'WebLLM extension is not installed or the model is not set.'; | ||
| 376 | default: | 381 | default: |
| 377 | return 'Check server console for more details'; | 382 | return 'Check server console for more details'; |
| 378 | } | 383 | } |
| @@ -747,10 +752,11 @@ async function getQueryText(chat, initiator) { | |||
| 747 | 752 | ||
| 748 | /** | 753 | /** |
| 749 | * Gets common body parameters for vector requests. | 754 | * Gets common body parameters for vector requests. |
| 750 | * @returns {object} | 755 | * @param {object} args Additional arguments |
| 756 | * @returns {object} Request body | ||
| 751 | */ | 757 | */ |
| 752 | function getVectorsRequestBody() { | 758 | function getVectorsRequestBody(args = {}) { |
| 753 | const body = {}; | 759 | const body = Object.assign({}, args); |
| 754 | switch (settings.source) { | 760 | switch (settings.source) { |
| 755 | case 'extras': | 761 | case 'extras': |
| 756 | body.extrasUrl = extension_settings.apiUrl; | 762 | body.extrasUrl = extension_settings.apiUrl; |
| @@ -777,6 +783,9 @@ function getVectorsRequestBody() { | |||
| 777 | body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.VLLM]; | 783 | body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.VLLM]; |
| 778 | body.model = extension_settings.vectors.vllm_model; | 784 | body.model = extension_settings.vectors.vllm_model; |
| 779 | break; | 785 | break; |
| 786 | case 'webllm': | ||
| 787 | body.model = extension_settings.vectors.webllm_model; | ||
| 788 | break; | ||
| 780 | default: | 789 | default: |
| 781 | break; | 790 | break; |
| 782 | } | 791 | } |
| @@ -784,6 +793,21 @@ function getVectorsRequestBody() { | |||
| 784 | } | 793 | } |
| 785 | 794 | ||
| 786 | /** | 795 | /** |
| 796 | * Gets additional arguments for vector requests. | ||
| 797 | * @param {string[]} items Items to embed | ||
| 798 | * @returns {Promise<object>} Additional arguments | ||
| 799 | */ | ||
| 800 | async function getAdditionalArgs(items) { | ||
| 801 | const args = {}; | ||
| 802 | switch (settings.source) { | ||
| 803 | case 'webllm': | ||
| 804 | args.embeddings = await createWebLlmEmbeddings(items); | ||
| 805 | break; | ||
| 806 | } | ||
| 807 | return args; | ||
| 808 | } | ||
| 809 | |||
| 810 | /** | ||
| 787 | * Gets the saved hashes for a collection | 811 | * Gets the saved hashes for a collection |
| 788 | * @param {string} collectionId | 812 | * @param {string} collectionId |
| 789 | * @returns {Promise<number[]>} Saved hashes | 813 | * @returns {Promise<number[]>} Saved hashes |
| @@ -816,11 +840,12 @@ async function getSavedHashes(collectionId) { | |||
| 816 | async function insertVectorItems(collectionId, items) { | 840 | async function insertVectorItems(collectionId, items) { |
| 817 | throwIfSourceInvalid(); | 841 | throwIfSourceInvalid(); |
| 818 | 842 | ||
| 843 | const args = await getAdditionalArgs(items.map(x => x.text)); | ||
| 819 | const response = await fetch('/api/vector/insert', { | 844 | const response = await fetch('/api/vector/insert', { |
| 820 | method: 'POST', | 845 | method: 'POST', |
| 821 | headers: getRequestHeaders(), | 846 | headers: getRequestHeaders(), |
| 822 | body: JSON.stringify({ | 847 | body: JSON.stringify({ |
| 823 | ...getVectorsRequestBody(), | 848 | ...getVectorsRequestBody(args), |
| 824 | collectionId: collectionId, | 849 | collectionId: collectionId, |
| 825 | items: items, | 850 | items: items, |
| 826 | source: settings.source, | 851 | source: settings.source, |
| @@ -858,6 +883,10 @@ function throwIfSourceInvalid() { | |||
| 858 | if (settings.source === 'extras' && !modules.includes('embeddings')) { | 883 | if (settings.source === 'extras' && !modules.includes('embeddings')) { |
| 859 | throw new Error('Vectors: Embeddings module missing', { cause: 'extras_module_missing' }); | 884 | throw new Error('Vectors: Embeddings module missing', { cause: 'extras_module_missing' }); |
| 860 | } | 885 | } |
| 886 | |||
| 887 | if (settings.source === 'webllm' && (!isWebLlmSupported() || !settings.webllm_model)) { | ||
| 888 | throw new Error('Vectors: WebLLM is not supported', { cause: 'webllm_not_supported' }); | ||
| 889 | } | ||
| 861 | } | 890 | } |
| 862 | 891 | ||
| 863 | /** | 892 | /** |
| @@ -890,11 +919,12 @@ async function deleteVectorItems(collectionId, hashes) { | |||
| 890 | * @returns {Promise<{ hashes: number[], metadata: object[]}>} - Hashes of the results | 919 | * @returns {Promise<{ hashes: number[], metadata: object[]}>} - Hashes of the results |
| 891 | */ | 920 | */ |
| 892 | async function queryCollection(collectionId, searchText, topK) { | 921 | async function queryCollection(collectionId, searchText, topK) { |
| 922 | const args = await getAdditionalArgs([searchText]); | ||
| 893 | const response = await fetch('/api/vector/query', { | 923 | const response = await fetch('/api/vector/query', { |
| 894 | method: 'POST', | 924 | method: 'POST', |
| 895 | headers: getRequestHeaders(), | 925 | headers: getRequestHeaders(), |
| 896 | body: JSON.stringify({ | 926 | body: JSON.stringify({ |
| 897 | ...getVectorsRequestBody(), | 927 | ...getVectorsRequestBody(args), |
| 898 | collectionId: collectionId, | 928 | collectionId: collectionId, |
| 899 | searchText: searchText, | 929 | searchText: searchText, |
| 900 | topK: topK, | 930 | topK: topK, |
| @@ -919,11 +949,12 @@ async function queryCollection(collectionId, searchText, topK) { | |||
| 919 | * @returns {Promise<Record<string, { hashes: number[], metadata: object[] }>>} - Results mapped to collection IDs | 949 | * @returns {Promise<Record<string, { hashes: number[], metadata: object[] }>>} - Results mapped to collection IDs |
| 920 | */ | 950 | */ |
| 921 | async function queryMultipleCollections(collectionIds, searchText, topK, threshold) { | 951 | async function queryMultipleCollections(collectionIds, searchText, topK, threshold) { |
| 952 | const args = await getAdditionalArgs([searchText]); | ||
| 922 | const response = await fetch('/api/vector/query-multi', { | 953 | const response = await fetch('/api/vector/query-multi', { |
| 923 | method: 'POST', | 954 | method: 'POST', |
| 924 | headers: getRequestHeaders(), | 955 | headers: getRequestHeaders(), |
| 925 | body: JSON.stringify({ | 956 | body: JSON.stringify({ |
| 926 | ...getVectorsRequestBody(), | 957 | ...getVectorsRequestBody(args), |
| 927 | collectionIds: collectionIds, | 958 | collectionIds: collectionIds, |
| 928 | searchText: searchText, | 959 | searchText: searchText, |
| 929 | topK: topK, | 960 | topK: topK, |
| @@ -1039,6 +1070,72 @@ function toggleSettings() { | |||
| 1039 | $('#llamacpp_vectorsModel').toggle(settings.source === 'llamacpp'); | 1070 | $('#llamacpp_vectorsModel').toggle(settings.source === 'llamacpp'); |
| 1040 | $('#vllm_vectorsModel').toggle(settings.source === 'vllm'); | 1071 | $('#vllm_vectorsModel').toggle(settings.source === 'vllm'); |
| 1041 | $('#nomicai_apiKey').toggle(settings.source === 'nomicai'); | 1072 | $('#nomicai_apiKey').toggle(settings.source === 'nomicai'); |
| 1073 | $('#webllm_vectorsModel').toggle(settings.source === 'webllm'); | ||
| 1074 | if (settings.source === 'webllm') { | ||
| 1075 | loadWebLlmModels(); | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | /** | ||
| 1080 | * Executes a function with WebLLM error handling. | ||
| 1081 | * @param {function(): Promise<T>} func Function to execute | ||
| 1082 | * @returns {Promise<T>} | ||
| 1083 | * @template T | ||
| 1084 | */ | ||
| 1085 | async function executeWithWebLlmErrorHandling(func) { | ||
| 1086 | try { | ||
| 1087 | return await func(); | ||
| 1088 | } catch (error) { | ||
| 1089 | console.log('Vectors: Failed to load WebLLM models', error); | ||
| 1090 | if (!(error instanceof Error)) { | ||
| 1091 | return; | ||
| 1092 | } | ||
| 1093 | switch (error.cause) { | ||
| 1094 | case 'webllm-not-available': | ||
| 1095 | toastr.warning('WebLLM is not available. Please install the extension.', 'WebLLM not installed'); | ||
| 1096 | break; | ||
| 1097 | case 'webllm-not-updated': | ||
| 1098 | toastr.warning('The installed extension version does not support embeddings.', 'WebLLM update required'); | ||
| 1099 | break; | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | /** | ||
| 1105 | * Loads and displays WebLLM models in the settings. | ||
| 1106 | * @returns {Promise<void>} | ||
| 1107 | */ | ||
| 1108 | function loadWebLlmModels() { | ||
| 1109 | return executeWithWebLlmErrorHandling(() => { | ||
| 1110 | const models = webllmProvider.getModels(); | ||
| 1111 | $('#vectors_webllm_model').empty(); | ||
| 1112 | for (const model of models) { | ||
| 1113 | $('#vectors_webllm_model').append($('<option>', { value: model.id, text: model.toString() })); | ||
| 1114 | } | ||
| 1115 | if (!settings.webllm_model || !models.some(x => x.id === settings.webllm_model)) { | ||
| 1116 | if (models.length) { | ||
| 1117 | settings.webllm_model = models[0].id; | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | $('#vectors_webllm_model').val(settings.webllm_model); | ||
| 1121 | return Promise.resolve(); | ||
| 1122 | }); | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | /** | ||
| 1126 | * Creates WebLLM embeddings for a list of items. | ||
| 1127 | * @param {string[]} items Items to embed | ||
| 1128 | * @returns {Promise<Record<string, number[]>>} Calculated embeddings | ||
| 1129 | */ | ||
| 1130 | async function createWebLlmEmbeddings(items) { | ||
| 1131 | return executeWithWebLlmErrorHandling(async () => { | ||
| 1132 | const embeddings = await webllmProvider.embedTexts(items, settings.webllm_model); | ||
| 1133 | const result = /** @type {Record<string, number[]>} */ ({}); | ||
| 1134 | for (let i = 0; i < items.length; i++) { | ||
| 1135 | result[items[i]] = embeddings[i]; | ||
| 1136 | } | ||
| 1137 | return result; | ||
| 1138 | }); | ||
| 1042 | } | 1139 | } |
| 1043 | 1140 | ||
| 1044 | async function onPurgeClick() { | 1141 | async function onPurgeClick() { |
| @@ -1567,6 +1664,30 @@ jQuery(async () => { | |||
| 1567 | $('#dialogue_popup_input').val(presetModel); | 1664 | $('#dialogue_popup_input').val(presetModel); |
| 1568 | }); | 1665 | }); |
| 1569 | 1666 | ||
| 1667 | $('#vectors_webllm_install').on('click', (e) => { | ||
| 1668 | e.preventDefault(); | ||
| 1669 | e.stopPropagation(); | ||
| 1670 | |||
| 1671 | if (Object.hasOwn(SillyTavern, 'llm')) { | ||
| 1672 | toastr.info('WebLLM is already installed'); | ||
| 1673 | return; | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | openThirdPartyExtensionMenu('https://github.com/SillyTavern/Extension-WebLLM'); | ||
| 1677 | }); | ||
| 1678 | |||
| 1679 | $('#vectors_webllm_model').on('input', () => { | ||
| 1680 | settings.webllm_model = String($('#vectors_webllm_model').val()); | ||
| 1681 | Object.assign(extension_settings.vectors, settings); | ||
| 1682 | saveSettingsDebounced(); | ||
| 1683 | }); | ||
| 1684 | |||
| 1685 | $('#vectors_webllm_load').on('click', async () => { | ||
| 1686 | if (!settings.webllm_model) return; | ||
| 1687 | await webllmProvider.loadModel(settings.webllm_model); | ||
| 1688 | toastr.success('WebLLM model loaded'); | ||
| 1689 | }); | ||
| 1690 | |||
| 1570 | $('#api_key_nomicai').toggleClass('success', !!secret_state[SECRET_KEYS.NOMICAI]); | 1691 | $('#api_key_nomicai').toggleClass('success', !!secret_state[SECRET_KEYS.NOMICAI]); |
| 1571 | 1692 | ||
| 1572 | toggleSettings(); | 1693 | toggleSettings(); |
| @@ -1578,6 +1699,11 @@ jQuery(async () => { | |||
| 1578 | eventSource.on(event_types.CHAT_DELETED, purgeVectorIndex); | 1699 | eventSource.on(event_types.CHAT_DELETED, purgeVectorIndex); |
| 1579 | eventSource.on(event_types.GROUP_CHAT_DELETED, purgeVectorIndex); | 1700 | eventSource.on(event_types.GROUP_CHAT_DELETED, purgeVectorIndex); |
| 1580 | eventSource.on(event_types.FILE_ATTACHMENT_DELETED, purgeFileVectorIndex); | 1701 | eventSource.on(event_types.FILE_ATTACHMENT_DELETED, purgeFileVectorIndex); |
| 1702 | eventSource.on(event_types.EXTENSION_SETTINGS_LOADED, async (manifest) => { | ||
| 1703 | if (settings.source === 'webllm' && manifest?.display_name === 'WebLLM') { | ||
| 1704 | await loadWebLlmModels(); | ||
| 1705 | } | ||
| 1706 | }); | ||
| 1581 | 1707 | ||
| 1582 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 1708 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1583 | name: 'db-ingest', | 1709 | name: 'db-ingest', |
| @@ -21,7 +21,23 @@ | |||
| 21 | <option value="openai">OpenAI</option> | 21 | <option value="openai">OpenAI</option> |
| 22 | <option value="togetherai">TogetherAI</option> | 22 | <option value="togetherai">TogetherAI</option> |
| 23 | <option value="vllm">vLLM</option> | 23 | <option value="vllm">vLLM</option> |
| 24 | <option value="webllm" data-i18n="WebLLM Extension">WebLLM Extension</option> | ||
| 25 | </select> | ||
| 26 | </div> | ||
| 27 | <div class="flex-container flexFlowColumn" id="webllm_vectorsModel"> | ||
| 28 | <label for="vectors_webllm_model" data-i18n="Vectorization Model"> | ||
| 29 | Vectorization Model | ||
| 30 | </label> | ||
| 31 | <div class="flex-container"> | ||
| 32 | <select id="vectors_webllm_model" class="text_pole flex1"> | ||
| 24 | </select> | 33 | </select> |
| 34 | <div id="vectors_webllm_load" class="menu_button menu_button_icon" title="Verify and load the selected model."> | ||
| 35 | <i class="fa-solid fa-check-to-slot"></i> | ||
| 36 | </div> | ||
| 37 | </div> | ||
| 38 | <div> | ||
| 39 | Requires the WebLLM extension to be installed. Click <a href="#" id="vectors_webllm_install">here</a> to install. | ||
| 40 | </div> | ||
| 25 | </div> | 41 | </div> |
| 26 | <div class="flex-container flexFlowColumn" id="ollama_vectorsModel"> | 42 | <div class="flex-container flexFlowColumn" id="ollama_vectorsModel"> |
| 27 | <label for="vectors_ollama_model" data-i18n="Vectorization Model"> | 43 | <label for="vectors_ollama_model" data-i18n="Vectorization Model"> |
| @@ -0,0 +1,64 @@ | |||
| 1 | export class WebLlmVectorProvider { | ||
| 2 | /** @type {object?} WebLLM engine */ | ||
| 3 | #engine = null; | ||
| 4 | |||
| 5 | constructor() { | ||
| 6 | this.#engine = null; | ||
| 7 | } | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Check if WebLLM is available and up-to-date | ||
| 11 | * @throws {Error} If WebLLM is not available or not up-to-date | ||
| 12 | */ | ||
| 13 | #checkWebLlm() { | ||
| 14 | if (!Object.hasOwn(SillyTavern, 'llm')) { | ||
| 15 | throw new Error('WebLLM is not available', { cause: 'webllm-not-available' }); | ||
| 16 | } | ||
| 17 | |||
| 18 | if (typeof SillyTavern.llm.generateEmbedding !== 'function') { | ||
| 19 | throw new Error('WebLLM is not updated', { cause: 'webllm-not-updated' }); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Initialize the engine with a model. | ||
| 25 | * @param {string} modelId Model ID to initialize the engine with | ||
| 26 | * @returns {Promise<void>} Promise that resolves when the engine is initialized | ||
| 27 | */ | ||
| 28 | #initEngine(modelId) { | ||
| 29 | this.#checkWebLlm(); | ||
| 30 | if (!this.#engine) { | ||
| 31 | this.#engine = SillyTavern.llm.getEngine(); | ||
| 32 | } | ||
| 33 | |||
| 34 | return this.#engine.loadModel(modelId); | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Get available models. | ||
| 39 | * @returns {{id:string, toString: function(): string}[]} Array of available models | ||
| 40 | */ | ||
| 41 | getModels() { | ||
| 42 | this.#checkWebLlm(); | ||
| 43 | return SillyTavern.llm.getEmbeddingModels(); | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Generate embeddings for a list of texts. | ||
| 48 | * @param {string[]} texts Array of texts to generate embeddings for | ||
| 49 | * @param {string} modelId Model to use for generating embeddings | ||
| 50 | * @returns {Promise<number[][]>} Array of embeddings for each text | ||
| 51 | */ | ||
| 52 | async embedTexts(texts, modelId) { | ||
| 53 | await this.#initEngine(modelId); | ||
| 54 | return this.#engine.generateEmbedding(texts); | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Loads a model into the engine. | ||
| 59 | * @param {string} modelId Model ID to load | ||
| 60 | */ | ||
| 61 | async loadModel(modelId) { | ||
| 62 | await this.#initEngine(modelId); | ||
| 63 | } | ||
| 64 | } | ||
| @@ -31,6 +31,7 @@ const SOURCES = [ | |||
| 31 | 'ollama', | 31 | 'ollama', |
| 32 | 'llamacpp', | 32 | 'llamacpp', |
| 33 | 'vllm', | 33 | 'vllm', |
| 34 | 'webllm', | ||
| 34 | ]; | 35 | ]; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| @@ -64,6 +65,8 @@ async function getVector(source, sourceSettings, text, isQuery, directories) { | |||
| 64 | return getVllmVector(text, sourceSettings.apiUrl, sourceSettings.model, directories); | 65 | return getVllmVector(text, sourceSettings.apiUrl, sourceSettings.model, directories); |
| 65 | case 'ollama': | 66 | case 'ollama': |
| 66 | return getOllamaVector(text, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories); | 67 | return getOllamaVector(text, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories); |
| 68 | case 'webllm': | ||
| 69 | return sourceSettings.embeddings[text]; | ||
| 67 | } | 70 | } |
| 68 | 71 | ||
| 69 | throw new Error(`Unknown vector source ${source}`); | 72 | throw new Error(`Unknown vector source ${source}`); |
| @@ -114,6 +117,9 @@ async function getBatchVector(source, sourceSettings, texts, isQuery, directorie | |||
| 114 | case 'ollama': | 117 | case 'ollama': |
| 115 | results.push(...await getOllamaBatchVector(batch, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories)); | 118 | results.push(...await getOllamaBatchVector(batch, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories)); |
| 116 | break; | 119 | break; |
| 120 | case 'webllm': | ||
| 121 | results.push(...texts.map(x => sourceSettings.embeddings[x])); | ||
| 122 | break; | ||
| 117 | default: | 123 | default: |
| 118 | throw new Error(`Unknown vector source ${source}`); | 124 | throw new Error(`Unknown vector source ${source}`); |
| 119 | } | 125 | } |
| @@ -179,6 +185,11 @@ function getSourceSettings(source, request) { | |||
| 179 | return { | 185 | return { |
| 180 | model: 'nomic-embed-text-v1.5', | 186 | model: 'nomic-embed-text-v1.5', |
| 181 | }; | 187 | }; |
| 188 | case 'webllm': | ||
| 189 | return { | ||
| 190 | model: String(request.body.model), | ||
| 191 | embeddings: request.body.embeddings ?? {}, | ||
| 192 | }; | ||
| 182 | default: | 193 | default: |
| 183 | return {}; | 194 | return {}; |
| 184 | } | 195 | } |