| import React, { useEffect, useState } from "react"; | |
| import System from "@/models/system"; | |
| import PreLoader from "@/components/Preloader"; | |
| import { LMSTUDIO_COMMON_URLS } from "@/utils/constants"; | |
| import { CaretDown, CaretUp } from "@phosphor-icons/react"; | |
| import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery"; | |
| export default function LMStudioEmbeddingOptions({ settings }) { | |
| const { | |
| autoDetecting: loading, | |
| basePath, | |
| basePathValue, | |
| showAdvancedControls, | |
| setShowAdvancedControls, | |
| handleAutoDetectClick, | |
| } = useProviderEndpointAutoDiscovery({ | |
| provider: "lmstudio", | |
| initialBasePath: settings?.EmbeddingBasePath, | |
| ENDPOINTS: LMSTUDIO_COMMON_URLS, | |
| }); | |
| const [maxChunkLength, setMaxChunkLength] = useState( | |
| settings?.EmbeddingModelMaxChunkLength || 8192 | |
| ); | |
| const handleMaxChunkLengthChange = (e) => { | |
| setMaxChunkLength(Number(e.target.value)); | |
| }; | |
| return ( | |
| <div className="w-full flex flex-col gap-y-7"> | |
| <div className="w-full flex items-start gap-[36px] mt-1.5"> | |
| <LMStudioModelSelection settings={settings} basePath={basePath.value} /> | |
| <div className="flex flex-col w-60"> | |
| <label className="text-white text-sm font-semibold block mb-2"> | |
| Max Embedding Chunk Length | |
| </label> | |
| <input | |
| type="number" | |
| name="EmbeddingModelMaxChunkLength" | |
| className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" | |
| placeholder="8192" | |
| min={1} | |
| value={maxChunkLength} | |
| onChange={handleMaxChunkLengthChange} | |
| onScroll={(e) => e.target.blur()} | |
| required={true} | |
| autoComplete="off" | |
| /> | |
| <p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2"> | |
| Maximum length of text chunks for embedding. | |
| </p> | |
| </div> | |
| </div> | |
| <div className="flex justify-start mt-4"> | |
| <button | |
| onClick={(e) => { | |
| e.preventDefault(); | |
| setShowAdvancedControls(!showAdvancedControls); | |
| }} | |
| className="border-none text-theme-text-primary hover:text-theme-text-secondary flex items-center text-sm" | |
| > | |
| {showAdvancedControls ? "Hide" : "Show"} Manual Endpoint Input | |
| {showAdvancedControls ? ( | |
| <CaretUp size={14} className="ml-1" /> | |
| ) : ( | |
| <CaretDown size={14} className="ml-1" /> | |
| )} | |
| </button> | |
| </div> | |
| <div hidden={!showAdvancedControls}> | |
| <div className="w-full flex items-start gap-4"> | |
| <div className="flex flex-col w-60"> | |
| <div className="flex justify-between items-center mb-2"> | |
| <label className="text-white text-sm font-semibold"> | |
| LM Studio Base URL | |
| </label> | |
| {loading ? ( | |
| <PreLoader size="6" /> | |
| ) : ( | |
| <> | |
| {!basePathValue.value && ( | |
| <button | |
| onClick={handleAutoDetectClick} | |
| className="bg-primary-button text-xs font-medium px-2 py-1 rounded-lg hover:bg-secondary hover:text-white shadow-[0_4px_14px_rgba(0,0,0,0.25)]" | |
| > | |
| Auto-Detect | |
| </button> | |
| )} | |
| </> | |
| )} | |
| </div> | |
| <input | |
| type="url" | |
| name="EmbeddingBasePath" | |
| className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" | |
| placeholder="http://localhost:1234/v1" | |
| value={basePathValue.value} | |
| required={true} | |
| autoComplete="off" | |
| spellCheck={false} | |
| onChange={basePath.onChange} | |
| onBlur={basePath.onBlur} | |
| /> | |
| <p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2"> | |
| Enter the URL where LM Studio is running. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function LMStudioModelSelection({ settings, basePath = null }) { | |
| const [customModels, setCustomModels] = useState([]); | |
| const [loading, setLoading] = useState(true); | |
| useEffect(() => { | |
| async function findCustomModels() { | |
| if (!basePath) { | |
| setCustomModels([]); | |
| setLoading(false); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const { models } = await System.customModels( | |
| "lmstudio", | |
| null, | |
| basePath | |
| ); | |
| setCustomModels(models || []); | |
| } catch (error) { | |
| console.error("Failed to fetch custom models:", error); | |
| setCustomModels([]); | |
| } | |
| setLoading(false); | |
| } | |
| findCustomModels(); | |
| }, [basePath]); | |
| if (loading || customModels.length == 0) { | |
| return ( | |
| <div className="flex flex-col w-60"> | |
| <label className="text-white text-sm font-semibold block mb-2"> | |
| LM Studio Embedding Model | |
| </label> | |
| <select | |
| name="EmbeddingModelPref" | |
| disabled={true} | |
| className="border-none bg-theme-settings-input-bg border-gray-500 text-white text-sm rounded-lg block w-full p-2.5" | |
| > | |
| <option disabled={true} selected={true}> | |
| {!!basePath | |
| ? "--loading available models--" | |
| : "Enter LM Studio URL first"} | |
| </option> | |
| </select> | |
| <p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2"> | |
| Select the LM Studio model for embeddings. Models will load after | |
| entering a valid LM Studio URL. | |
| </p> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="flex flex-col w-60"> | |
| <label className="text-white text-sm font-semibold block mb-2"> | |
| LM Studio Embedding Model | |
| </label> | |
| <select | |
| name="EmbeddingModelPref" | |
| required={true} | |
| className="border-none bg-theme-settings-input-bg border-gray-500 text-white text-sm rounded-lg block w-full p-2.5" | |
| > | |
| {customModels.length > 0 && ( | |
| <optgroup label="Your loaded models"> | |
| {customModels.map((model) => { | |
| return ( | |
| <option | |
| key={model.id} | |
| value={model.id} | |
| selected={settings.EmbeddingModelPref === model.id} | |
| > | |
| {model.id} | |
| </option> | |
| ); | |
| })} | |
| </optgroup> | |
| )} | |
| </select> | |
| <p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2"> | |
| Choose the LM Studio model you want to use for generating embeddings. | |
| </p> | |
| </div> | |
| ); | |
| } | |