diff --git a/.assets/perplexica-screenshot.png b/.assets/perplexica-screenshot.png index 0643098..0bea5d0 100644 Binary files a/.assets/perplexica-screenshot.png and b/.assets/perplexica-screenshot.png differ diff --git a/README.md b/README.md index ce2db8b..33e62e6 100644 --- a/README.md +++ b/README.md @@ -79,19 +79,19 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. Perplexica can be easily run using Docker. Simply run the following command: ```bash -docker run -p 3000:3000 --name perplexica itzcrazykns1337/perplexica:latest +docker run -d -p 3000:3000 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:latest ``` This will pull and start the Perplexica container with the bundled SearxNG search engine. Once running, open your browser and navigate to http://localhost:3000. You can then configure your settings (API keys, models, etc.) directly in the setup screen. -**Note**: The image includes both Perplexica and SearxNG, so no additional setup is required. +**Note**: The image includes both Perplexica and SearxNG, so no additional setup is required. The `-v` flags create persistent volumes for your data and uploaded files. #### Using Perplexica with Your Own SearxNG Instance If you already have SearxNG running, you can use the slim version of Perplexica: ```bash -docker run -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 --name perplexica itzcrazykns1337/perplexica:slim-latest +docker run -d -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:slim-latest ``` **Important**: Make sure your SearxNG instance has: @@ -118,7 +118,7 @@ If you prefer to build from source or need more control: ```bash docker build -t perplexica . - docker run -p 3000:3000 --name perplexica perplexica + docker run -d -p 3000:3000 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica perplexica ``` 5. Access Perplexica at http://localhost:3000 and configure your settings in the setup screen. diff --git a/app.dockerfile b/app.dockerfile deleted file mode 100644 index 92358e7..0000000 --- a/app.dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM node:24.5.0-slim AS builder - -RUN apt-get update && apt-get install -y python3 python3-pip sqlite3 && rm -rf /var/lib/apt/lists/* - -WORKDIR /home/perplexica - -COPY package.json yarn.lock ./ -RUN yarn install --frozen-lockfile --network-timeout 600000 - -COPY tsconfig.json next.config.mjs next-env.d.ts postcss.config.js drizzle.config.ts tailwind.config.ts ./ -COPY src ./src -COPY public ./public -COPY drizzle ./drizzle - -RUN mkdir -p /home/perplexica/data -RUN yarn build - -FROM node:24.5.0-slim - -RUN apt-get update && apt-get install -y python3 python3-pip sqlite3 && rm -rf /var/lib/apt/lists/* - -WORKDIR /home/perplexica - -COPY --from=builder /home/perplexica/public ./public -COPY --from=builder /home/perplexica/.next/static ./public/_next/static - -COPY --from=builder /home/perplexica/.next/standalone ./ -COPY --from=builder /home/perplexica/data ./data -COPY drizzle ./drizzle - -RUN mkdir /home/perplexica/uploads - -COPY entrypoint.sh ./entrypoint.sh -RUN chmod +x ./entrypoint.sh -RUN sed -i 's/\r$//' ./entrypoint.sh || true - -CMD ["/home/perplexica/entrypoint.sh"] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..50b6785 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,15 @@ +services: + perplexica: + image: itzcrazykns1337/perplexica:latest + ports: + - '3000:3000' + volumes: + - data:/home/perplexica/data + - uploads:/home/perplexica/uploads + restart: unless-stopped + +volumes: + data: + name: 'perplexica-data' + uploads: + name: 'perplexica-uploads' diff --git a/docs/API/SEARCH.md b/docs/API/SEARCH.md index bf0db7a..04f11ef 100644 --- a/docs/API/SEARCH.md +++ b/docs/API/SEARCH.md @@ -17,6 +17,7 @@ Before making search requests, you'll need to get the available providers and th Returns a list of all active providers with their available chat and embedding models. **Response Example:** + ```json { "providers": [ diff --git a/docs/installation/UPDATING.md b/docs/installation/UPDATING.md index daa1122..0603671 100644 --- a/docs/installation/UPDATING.md +++ b/docs/installation/UPDATING.md @@ -10,7 +10,7 @@ Simply pull the latest image and restart your container: docker pull itzcrazykns1337/perplexica:latest docker stop perplexica docker rm perplexica -docker run -p 3000:3000 --name perplexica itzcrazykns1337/perplexica:latest +docker run -d -p 3000:3000 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:latest ``` For slim version: @@ -19,7 +19,7 @@ For slim version: docker pull itzcrazykns1337/perplexica:slim-latest docker stop perplexica docker rm perplexica -docker run -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 --name perplexica itzcrazykns1337/perplexica:slim-latest +docker run -d -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:slim-latest ``` Once updated, go to http://localhost:3000 and verify the latest changes. Your settings are preserved automatically. diff --git a/package.json b/package.json index 3bfe63f..7083b66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "perplexica-frontend", - "version": "1.11.0", + "version": "1.11.2", "license": "MIT", "author": "ItzCrazyKns", "scripts": { diff --git a/src/app/c/[chatId]/page.tsx b/src/app/c/[chatId]/page.tsx index 672107a..39b93f0 100644 --- a/src/app/c/[chatId]/page.tsx +++ b/src/app/c/[chatId]/page.tsx @@ -1,17 +1,10 @@ 'use client'; import ChatWindow from '@/components/ChatWindow'; -import { useParams } from 'next/navigation'; import React from 'react'; -import { ChatProvider } from '@/lib/hooks/useChat'; const Page = () => { - const { chatId }: { chatId: string } = useParams(); - return ( - - - - ); + return ; }; export default Page; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 830d842..e9fd8c7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -9,6 +9,7 @@ import { Toaster } from 'sonner'; import ThemeProvider from '@/components/theme/Provider'; import configManager from '@/lib/config'; import SetupWizard from '@/components/Setup/SetupWizard'; +import { ChatProvider } from '@/lib/hooks/useChat'; const montserrat = Montserrat({ weight: ['300', '400', '500', '700'], @@ -36,7 +37,7 @@ export default function RootLayout({ {setupComplete ? ( - <> + {children} - + ) : ( )} diff --git a/src/app/page.tsx b/src/app/page.tsx index 25981b5..2f3bd36 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,5 @@ import ChatWindow from '@/components/ChatWindow'; -import { ChatProvider } from '@/lib/hooks/useChat'; import { Metadata } from 'next'; -import { Suspense } from 'react'; export const metadata: Metadata = { title: 'Chat - Perplexica', @@ -9,15 +7,7 @@ export const metadata: Metadata = { }; const Home = () => { - return ( -
- - - - - -
- ); + return ; }; export default Home; diff --git a/src/components/ChatWindow.tsx b/src/components/ChatWindow.tsx index 5c0a3e8..c04b4ea 100644 --- a/src/components/ChatWindow.tsx +++ b/src/components/ChatWindow.tsx @@ -9,6 +9,7 @@ import Link from 'next/link'; import NextError from 'next/error'; import { useChat } from '@/lib/hooks/useChat'; import Loader from './ui/Loader'; +import SettingsButtonMobile from './Settings/SettingsButtonMobile'; export interface BaseMessage { chatId: string; @@ -56,9 +57,7 @@ const ChatWindow = () => { return (
- - - +

diff --git a/src/components/MessageInputActions/ChatModelSelector.tsx b/src/components/MessageInputActions/ChatModelSelector.tsx index afc3b3b..ddad581 100644 --- a/src/components/MessageInputActions/ChatModelSelector.tsx +++ b/src/components/MessageInputActions/ChatModelSelector.tsx @@ -8,17 +8,16 @@ import { PopoverPanel, Transition, } from '@headlessui/react'; -import { Fragment, useEffect, useState } from 'react'; +import { Fragment, useEffect, useMemo, useState } from 'react'; import { MinimalProvider } from '@/lib/models/types'; +import { useChat } from '@/lib/hooks/useChat'; const ModelSelector = () => { const [providers, setProviders] = useState([]); const [isLoading, setIsLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); - const [selectedModel, setSelectedModel] = useState<{ - providerId: string; - modelKey: string; - } | null>(null); + + const { setChatModelProvider, chatModelProvider } = useChat(); useEffect(() => { const loadProviders = async () => { @@ -30,28 +29,8 @@ const ModelSelector = () => { throw new Error('Failed to fetch providers'); } - const data = await res.json(); - setProviders(data.providers || []); - - const savedProviderId = localStorage.getItem('chatModelProviderId'); - const savedModelKey = localStorage.getItem('chatModelKey'); - - if (savedProviderId && savedModelKey) { - setSelectedModel({ - providerId: savedProviderId, - modelKey: savedModelKey, - }); - } else if (data.providers && data.providers.length > 0) { - const firstProvider = data.providers.find( - (p: MinimalProvider) => p.chatModels.length > 0, - ); - if (firstProvider && firstProvider.chatModels[0]) { - setSelectedModel({ - providerId: firstProvider.id, - modelKey: firstProvider.chatModels[0].key, - }); - } - } + const data: { providers: MinimalProvider[] } = await res.json(); + setProviders(data.providers); } catch (error) { console.error('Error loading providers:', error); } finally { @@ -62,13 +41,32 @@ const ModelSelector = () => { loadProviders(); }, []); + const orderedProviders = useMemo(() => { + if (!chatModelProvider?.providerId) return providers; + + const currentProviderIndex = providers.findIndex( + (p) => p.id === chatModelProvider.providerId, + ); + + if (currentProviderIndex === -1) { + return providers; + } + + const selectedProvider = providers[currentProviderIndex]; + const remainingProviders = providers.filter( + (_, index) => index !== currentProviderIndex, + ); + + return [selectedProvider, ...remainingProviders]; + }, [providers, chatModelProvider]); + const handleModelSelect = (providerId: string, modelKey: string) => { - setSelectedModel({ providerId, modelKey }); + setChatModelProvider({ providerId, key: modelKey }); localStorage.setItem('chatModelProviderId', providerId); localStorage.setItem('chatModelKey', modelKey); }; - const filteredProviders = providers + const filteredProviders = orderedProviders .map((provider) => ({ ...provider, chatModels: provider.chatModels.filter( @@ -140,15 +138,16 @@ const ModelSelector = () => {

{provider.chatModels.map((model) => ( - handleModelSelect(provider.id, model.key) } + type="button" className={cn( 'px-3 py-2 flex items-center justify-between text-start duration-200 cursor-pointer transition rounded-lg group', - selectedModel?.providerId === provider.id && - selectedModel?.modelKey === model.key + chatModelProvider?.providerId === provider.id && + chatModelProvider?.key === model.key ? 'bg-light-secondary dark:bg-dark-secondary' : 'hover:bg-light-secondary dark:hover:bg-dark-secondary', )} @@ -158,8 +157,9 @@ const ModelSelector = () => { size={15} className={cn( 'shrink-0', - selectedModel?.providerId === provider.id && - selectedModel?.modelKey === model.key + chatModelProvider?.providerId === + provider.id && + chatModelProvider?.key === model.key ? 'text-sky-500' : 'text-black/50 dark:text-white/50 group-hover:text-black/70 group-hover:dark:text-white/70', )} @@ -167,8 +167,9 @@ const ModelSelector = () => {

{ {model.name}

- + ))}
diff --git a/src/components/Settings/Sections/Models/ModelProvider.tsx b/src/components/Settings/Sections/Models/ModelProvider.tsx index 8a2b0f6..79928c2 100644 --- a/src/components/Settings/Sections/Models/ModelProvider.tsx +++ b/src/components/Settings/Sections/Models/ModelProvider.tsx @@ -112,100 +112,96 @@ const ModelProvider = ({ >
- {modelProvider.chatModels.length > 0 && ( -
-
-

- Chat models -

- -
-
- {modelProvider.chatModels.some((m) => m.key === 'error') ? ( -
- - - { - modelProvider.chatModels.find( - (m) => m.key === 'error', - )?.name - } - -
- ) : ( -
- {modelProvider.chatModels.map((model, index) => ( -
- {model.name} - -
- ))} -
- )} -
+
+
+

+ Chat models +

+
- )} - {modelProvider.embeddingModels.length > 0 && ( -
-
-

- Embedding models -

- -
-
- {modelProvider.embeddingModels.some( - (m) => m.key === 'error', - ) ? ( -
- - - { - modelProvider.embeddingModels.find( - (m) => m.key === 'error', - )?.name - } - -
- ) : ( -
- {modelProvider.embeddingModels.map((model, index) => ( -
+ {modelProvider.chatModels.some((m) => m.key === 'error') ? ( +
+ + + { + modelProvider.chatModels.find( + (m) => m.key === 'error', + )?.name + } + +
+ ) : ( +
+ {modelProvider.chatModels.map((model, index) => ( +
+ {model.name} + -
- ))} -
- )} -
+ + +
+ ))} +
+ )}
- )} +
+
+
+

+ Embedding models +

+ +
+
+ {modelProvider.embeddingModels.some( + (m) => m.key === 'error', + ) ? ( +
+ + + { + modelProvider.embeddingModels.find( + (m) => m.key === 'error', + )?.name + } + +
+ ) : ( +
+ {modelProvider.embeddingModels.map((model, index) => ( +
+ {model.name} + +
+ ))} +
+ )} +
+
)} diff --git a/src/components/Settings/Sections/Models/ModelSelect.tsx b/src/components/Settings/Sections/Models/ModelSelect.tsx index 13dd3b4..75117b3 100644 --- a/src/components/Settings/Sections/Models/ModelSelect.tsx +++ b/src/components/Settings/Sections/Models/ModelSelect.tsx @@ -1,5 +1,6 @@ import Select from '@/components/ui/Select'; import { ConfigModelProvider } from '@/lib/config/types'; +import { useChat } from '@/lib/hooks/useChat'; import { useState } from 'react'; import { toast } from 'sonner'; @@ -11,30 +12,40 @@ const ModelSelect = ({ type: 'chat' | 'embedding'; }) => { const [selectedModel, setSelectedModel] = useState( - `${providers[0]?.id}/${providers[0].embeddingModels[0]?.key}`, + type === 'chat' + ? `${localStorage.getItem('chatModelProviderId')}/${localStorage.getItem('chatModelKey')}` + : `${localStorage.getItem('embeddingModelProviderId')}/${localStorage.getItem('embeddingModelKey')}`, ); const [loading, setLoading] = useState(false); + const { setChatModelProvider, setEmbeddingModelProvider } = useChat(); const handleSave = async (newValue: string) => { setLoading(true); setSelectedModel(newValue); - console.log(newValue); + try { if (type === 'chat') { - localStorage.setItem('chatModelProviderId', newValue.split('/')[0]); - localStorage.setItem( - 'chatModelKey', - newValue.split('/').slice(1).join('/'), - ); + const providerId = newValue.split('/')[0]; + const modelKey = newValue.split('/').slice(1).join('/'); + + localStorage.setItem('chatModelProviderId', providerId); + localStorage.setItem('chatModelKey', modelKey); + + setChatModelProvider({ + providerId: providerId, + key: modelKey, + }); } else { - localStorage.setItem( - 'embeddingModelProviderId', - newValue.split('/')[0], - ); - localStorage.setItem( - 'embeddingModelKey', - newValue.split('/').slice(1).join('/'), - ); + const providerId = newValue.split('/')[0]; + const modelKey = newValue.split('/').slice(1).join('/'); + + localStorage.setItem('embeddingModelProviderId', providerId); + localStorage.setItem('embeddingModelKey', modelKey); + + setEmbeddingModelProvider({ + providerId: providerId, + key: modelKey, + }); } } catch (error) { console.error('Error saving config:', error); diff --git a/src/components/Settings/SettingsButtonMobile.tsx b/src/components/Settings/SettingsButtonMobile.tsx index 836a4eb..db26f4c 100644 --- a/src/components/Settings/SettingsButtonMobile.tsx +++ b/src/components/Settings/SettingsButtonMobile.tsx @@ -9,7 +9,7 @@ const SettingsButtonMobile = () => { return ( <> {isOpen && } diff --git a/src/components/Setup/SetupConfig.tsx b/src/components/Setup/SetupConfig.tsx index 18b07e9..334974f 100644 --- a/src/components/Setup/SetupConfig.tsx +++ b/src/components/Setup/SetupConfig.tsx @@ -63,8 +63,7 @@ const SetupConfig = ({ } }; - const hasProviders = - providers.filter((p) => p.chatModels.length > 0).length > 0; + const hasProviders = providers.length > 0; return (
diff --git a/src/lib/config/index.ts b/src/lib/config/index.ts index ce1a81e..0487a11 100644 --- a/src/lib/config/index.ts +++ b/src/lib/config/index.ts @@ -50,6 +50,25 @@ class ConfigManager { 'e.g., "Respond in a friendly and concise tone" or "Use British English and format answers as bullet points."', scope: 'client', }, + { + name: 'Measurement Unit', + key: 'measureUnit', + type: 'select', + options: [ + { + name: 'Imperial', + value: 'Imperial', + }, + { + name: 'Metric', + value: 'Metric', + }, + ], + required: false, + description: 'Choose between Metric and Imperial measurement unit.', + default: 'Metric', + scope: 'client', + }, ], modelProviders: [], search: [ diff --git a/src/lib/hooks/useChat.tsx b/src/lib/hooks/useChat.tsx index 04b17b5..8ef57ef 100644 --- a/src/lib/hooks/useChat.tsx +++ b/src/lib/hooks/useChat.tsx @@ -17,7 +17,7 @@ import { useState, } from 'react'; import crypto from 'crypto'; -import { useSearchParams } from 'next/navigation'; +import { useParams, useSearchParams } from 'next/navigation'; import { toast } from 'sonner'; import { getSuggestions } from '../actions'; import { MinimalProvider } from '../models/types'; @@ -48,6 +48,8 @@ type ChatContext = { messageAppeared: boolean; isReady: boolean; hasError: boolean; + chatModelProvider: ChatModelProvider; + embeddingModelProvider: EmbeddingModelProvider; setOptimizationMode: (mode: string) => void; setFocusMode: (mode: string) => void; setFiles: (files: File[]) => void; @@ -58,6 +60,8 @@ type ChatContext = { rewrite?: boolean, ) => Promise; rewrite: (messageId: string) => void; + setChatModelProvider: (provider: ChatModelProvider) => void; + setEmbeddingModelProvider: (provider: EmbeddingModelProvider) => void; }; export interface File { @@ -256,25 +260,24 @@ export const chatContext = createContext({ sections: [], notFound: false, optimizationMode: '', + chatModelProvider: { key: '', providerId: '' }, + embeddingModelProvider: { key: '', providerId: '' }, rewrite: () => {}, sendMessage: async () => {}, setFileIds: () => {}, setFiles: () => {}, setFocusMode: () => {}, setOptimizationMode: () => {}, + setChatModelProvider: () => {}, + setEmbeddingModelProvider: () => {}, }); -export const ChatProvider = ({ - children, - id, -}: { - children: React.ReactNode; - id?: string; -}) => { +export const ChatProvider = ({ children }: { children: React.ReactNode }) => { + const params: { chatId: string } = useParams(); const searchParams = useSearchParams(); const initialMessage = searchParams.get('q'); - const [chatId, setChatId] = useState(id); + const [chatId, setChatId] = useState(params.chatId); const [newChatCreated, setNewChatCreated] = useState(false); const [loading, setLoading] = useState(false); @@ -443,6 +446,19 @@ export const ChatProvider = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (params.chatId && params.chatId !== chatId) { + setChatId(params.chatId); + setMessages([]); + setChatHistory([]); + setFiles([]); + setFileIds([]); + setIsMessagesLoaded(false); + setNotFound(false); + setNewChatCreated(false); + } + }, [params.chatId, chatId]); + useEffect(() => { if ( chatId && @@ -466,7 +482,7 @@ export const ChatProvider = ({ setChatId(crypto.randomBytes(20).toString('hex')); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [chatId, isMessagesLoaded, newChatCreated, messages.length]); useEffect(() => { messagesRef.current = messages; @@ -519,7 +535,7 @@ export const ChatProvider = ({ messageId, rewrite = false, ) => { - if (loading) return; + if (loading || !message) return; setLoading(true); setMessageAppeared(false); @@ -743,6 +759,10 @@ export const ChatProvider = ({ setOptimizationMode, rewrite, sendMessage, + setChatModelProvider, + chatModelProvider, + embeddingModelProvider, + setEmbeddingModelProvider, }} > {children} diff --git a/src/lib/huggingfaceTransformer.ts b/src/lib/huggingfaceTransformer.ts deleted file mode 100644 index a8c21bd..0000000 --- a/src/lib/huggingfaceTransformer.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Embeddings, type EmbeddingsParams } from '@langchain/core/embeddings'; -import { chunkArray } from '@langchain/core/utils/chunk_array'; - -export interface HuggingFaceTransformersEmbeddingsParams - extends EmbeddingsParams { - modelName: string; - - model: string; - - timeout?: number; - - batchSize?: number; - - stripNewLines?: boolean; -} - -export class HuggingFaceTransformersEmbeddings - extends Embeddings - implements HuggingFaceTransformersEmbeddingsParams -{ - modelName = 'Xenova/all-MiniLM-L6-v2'; - - model = 'Xenova/all-MiniLM-L6-v2'; - - batchSize = 512; - - stripNewLines = true; - - timeout?: number; - - constructor(fields?: Partial) { - super(fields ?? {}); - - this.modelName = fields?.model ?? fields?.modelName ?? this.model; - this.model = this.modelName; - this.stripNewLines = fields?.stripNewLines ?? this.stripNewLines; - this.timeout = fields?.timeout; - } - - async embedDocuments(texts: string[]): Promise { - const batches = chunkArray( - this.stripNewLines ? texts.map((t) => t.replace(/\n/g, ' ')) : texts, - this.batchSize, - ); - - const batchRequests = batches.map((batch) => this.runEmbedding(batch)); - const batchResponses = await Promise.all(batchRequests); - const embeddings: number[][] = []; - - for (let i = 0; i < batchResponses.length; i += 1) { - const batchResponse = batchResponses[i]; - for (let j = 0; j < batchResponse.length; j += 1) { - embeddings.push(batchResponse[j]); - } - } - - return embeddings; - } - - async embedQuery(text: string): Promise { - const data = await this.runEmbedding([ - this.stripNewLines ? text.replace(/\n/g, ' ') : text, - ]); - return data[0]; - } - - private async runEmbedding(texts: string[]) { - const { pipeline } = await import('@huggingface/transformers'); - const pipe = await pipeline('feature-extraction', this.model); - - return this.caller.call(async () => { - const output = await pipe(texts, { pooling: 'mean', normalize: true }); - return output.tolist(); - }); - } -} diff --git a/src/lib/models/providers/transformers.ts b/src/lib/models/providers/transformers.ts index 1468155..afd6b9e 100644 --- a/src/lib/models/providers/transformers.ts +++ b/src/lib/models/providers/transformers.ts @@ -4,8 +4,7 @@ import BaseModelProvider from './baseProvider'; import { Embeddings } from '@langchain/core/embeddings'; import { UIConfigField } from '@/lib/config/types'; import { getConfiguredModelProviderById } from '@/lib/config/serverRegistry'; -import { HuggingFaceTransformersEmbeddings } from '@/lib/huggingfaceTransformer'; - +import { HuggingFaceTransformersEmbeddings } from '@langchain/community/embeddings/huggingface_transformers'; interface TransformersConfig {} const defaultEmbeddingModels: Model[] = [