mirror of
				https://github.com/ItzCrazyKns/Perplexica.git
				synced 2025-10-31 19:38:13 +00:00 
			
		
		
		
	feat(app): rename providers to connection, enhance UX
This commit is contained in:
		| @@ -82,10 +82,10 @@ const AddProvider = ({ | |||||||
|  |  | ||||||
|       setProviders((prev) => [...prev, data]); |       setProviders((prev) => [...prev, data]); | ||||||
|  |  | ||||||
|       toast.success('Provider added successfully.'); |       toast.success('Connection added successfully.'); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       console.error('Error adding provider:', error); |       console.error('Error adding provider:', error); | ||||||
|       toast.error('Failed to add provider.'); |       toast.error('Failed to add connection.'); | ||||||
|     } finally { |     } finally { | ||||||
|       setLoading(false); |       setLoading(false); | ||||||
|       setOpen(false); |       setOpen(false); | ||||||
| @@ -99,7 +99,7 @@ const AddProvider = ({ | |||||||
|         className="px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-xs sm:text-sm border border-light-200 dark:border-dark-200 text-black dark:text-white bg-light-secondary/50 dark:bg-dark-secondary/50 hover:bg-light-secondary hover:dark:bg-dark-secondary hover:border-light-300 hover:dark:border-dark-300 flex flex-row items-center space-x-1 active:scale-95 transition duration-200" |         className="px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-xs sm:text-sm border border-light-200 dark:border-dark-200 text-black dark:text-white bg-light-secondary/50 dark:bg-dark-secondary/50 hover:bg-light-secondary hover:dark:bg-dark-secondary hover:border-light-300 hover:dark:border-dark-300 flex flex-row items-center space-x-1 active:scale-95 transition duration-200" | ||||||
|       > |       > | ||||||
|         <Plus className="w-3.5 h-3.5 md:w-4 md:h-4" /> |         <Plus className="w-3.5 h-3.5 md:w-4 md:h-4" /> | ||||||
|         <span>Add Provider</span> |         <span>Add Connection</span> | ||||||
|       </button> |       </button> | ||||||
|       <AnimatePresence> |       <AnimatePresence> | ||||||
|         {open && ( |         {open && ( | ||||||
| @@ -120,7 +120,7 @@ const AddProvider = ({ | |||||||
|                 <form onSubmit={handleSubmit} className="flex flex-col flex-1"> |                 <form onSubmit={handleSubmit} className="flex flex-col flex-1"> | ||||||
|                   <div className="px-6 pt-6 pb-4"> |                   <div className="px-6 pt-6 pb-4"> | ||||||
|                     <h3 className="text-black/90 dark:text-white/90 font-medium"> |                     <h3 className="text-black/90 dark:text-white/90 font-medium"> | ||||||
|                       Add new provider |                       Add new connection | ||||||
|                     </h3> |                     </h3> | ||||||
|                   </div> |                   </div> | ||||||
|                   <div className="border-t border-light-200 dark:border-dark-200" /> |                   <div className="border-t border-light-200 dark:border-dark-200" /> | ||||||
| @@ -128,7 +128,7 @@ const AddProvider = ({ | |||||||
|                     <div className="flex flex-col space-y-4"> |                     <div className="flex flex-col space-y-4"> | ||||||
|                       <div className="flex flex-col items-start space-y-2"> |                       <div className="flex flex-col items-start space-y-2"> | ||||||
|                         <label className="text-xs text-black/70 dark:text-white/70"> |                         <label className="text-xs text-black/70 dark:text-white/70"> | ||||||
|                           Select provider type |                           Select connection type | ||||||
|                         </label> |                         </label> | ||||||
|                         <Select |                         <Select | ||||||
|                           value={selectedProvider ?? ''} |                           value={selectedProvider ?? ''} | ||||||
| @@ -149,13 +149,13 @@ const AddProvider = ({ | |||||||
|                         className="flex flex-col items-start space-y-2" |                         className="flex flex-col items-start space-y-2" | ||||||
|                       > |                       > | ||||||
|                         <label className="text-xs text-black/70 dark:text-white/70"> |                         <label className="text-xs text-black/70 dark:text-white/70"> | ||||||
|                           Name* |                           Connection Name* | ||||||
|                         </label> |                         </label> | ||||||
|                         <input |                         <input | ||||||
|                           value={name} |                           value={name} | ||||||
|                           onChange={(e) => setName(e.target.value)} |                           onChange={(e) => setName(e.target.value)} | ||||||
|                           className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60" |                           className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60" | ||||||
|                           placeholder={'Provider Name'} |                           placeholder={'e.g., My OpenAI Connection'} | ||||||
|                           type="text" |                           type="text" | ||||||
|                           required={true} |                           required={true} | ||||||
|                         /> |                         /> | ||||||
| @@ -199,7 +199,7 @@ const AddProvider = ({ | |||||||
|                       {loading ? ( |                       {loading ? ( | ||||||
|                         <Loader2 className="animate-spin" size={16} /> |                         <Loader2 className="animate-spin" size={16} /> | ||||||
|                       ) : ( |                       ) : ( | ||||||
|                         'Add Provider' |                         'Add Connection' | ||||||
|                       )} |                       )} | ||||||
|                     </button> |                     </button> | ||||||
|                   </div> |                   </div> | ||||||
|   | |||||||
| @@ -34,10 +34,10 @@ const DeleteProvider = ({ | |||||||
|         return prev.filter((p) => p.id !== modelProvider.id); |         return prev.filter((p) => p.id !== modelProvider.id); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       toast.success('Provider deleted successfully.'); |       toast.success('Connection deleted successfully.'); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       console.error('Error deleting provider:', error); |       console.error('Error deleting provider:', error); | ||||||
|       toast.error('Failed to delete provider.'); |       toast.error('Failed to delete connection.'); | ||||||
|     } finally { |     } finally { | ||||||
|       setLoading(false); |       setLoading(false); | ||||||
|     } |     } | ||||||
| @@ -51,7 +51,7 @@ const DeleteProvider = ({ | |||||||
|           setOpen(true); |           setOpen(true); | ||||||
|         }} |         }} | ||||||
|         className="group p-1.5 rounded-md hover:bg-light-200 hover:dark:bg-dark-200 transition-colors group" |         className="group p-1.5 rounded-md hover:bg-light-200 hover:dark:bg-dark-200 transition-colors group" | ||||||
|         title="Delete provider" |         title="Delete connection" | ||||||
|       > |       > | ||||||
|         <Trash2 |         <Trash2 | ||||||
|           size={14} |           size={14} | ||||||
| @@ -76,14 +76,15 @@ const DeleteProvider = ({ | |||||||
|               <DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg"> |               <DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg"> | ||||||
|                 <div className="px-6 pt-6 pb-4"> |                 <div className="px-6 pt-6 pb-4"> | ||||||
|                   <h3 className="text-black/90 dark:text-white/90 font-medium"> |                   <h3 className="text-black/90 dark:text-white/90 font-medium"> | ||||||
|                     Delete provider |                     Delete connection | ||||||
|                   </h3> |                   </h3> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div className="border-t border-light-200 dark:border-dark-200" /> |                 <div className="border-t border-light-200 dark:border-dark-200" /> | ||||||
|                 <div className="flex-1 overflow-y-auto px-6 py-4"> |                 <div className="flex-1 overflow-y-auto px-6 py-4"> | ||||||
|                   <p className="text-SM text-black/60 dark:text-white/60"> |                   <p className="text-sm text-black/60 dark:text-white/60"> | ||||||
|                     Are you sure you want to delete the provider " |                     Are you sure you want to delete the connection " | ||||||
|                     {modelProvider.name}"? This action cannot be undone. |                     {modelProvider.name}"? This action cannot be undone. | ||||||
|  |                     All associated models will also be removed. | ||||||
|                   </p> |                   </p> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div className="px-6 py-6 flex justify-end space-x-2"> |                 <div className="px-6 py-6 flex justify-end space-x-2"> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import { UIConfigField, ConfigModelProvider } from '@/lib/config/types'; | import { UIConfigField, ConfigModelProvider } from '@/lib/config/types'; | ||||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||||
| import { AnimatePresence, motion } from 'framer-motion'; | import { AnimatePresence, motion } from 'framer-motion'; | ||||||
| import { AlertCircle, ChevronDown, Pencil, Trash2, X } from 'lucide-react'; | import { AlertCircle, Plug2, Plus, Pencil, Trash2, X } from 'lucide-react'; | ||||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||||
| import { toast } from 'sonner'; | import { toast } from 'sonner'; | ||||||
| import AddModel from './AddModelDialog'; | import AddModel from './AddModelDialog'; | ||||||
| @@ -17,7 +17,7 @@ const ModelProvider = ({ | |||||||
|   fields: UIConfigField[]; |   fields: UIConfigField[]; | ||||||
|   setProviders: React.Dispatch<React.SetStateAction<ConfigModelProvider[]>>; |   setProviders: React.Dispatch<React.SetStateAction<ConfigModelProvider[]>>; | ||||||
| }) => { | }) => { | ||||||
|   const [open, setOpen] = useState(false); |   const [open, setOpen] = useState(true); | ||||||
|  |  | ||||||
|   const handleModelDelete = async ( |   const handleModelDelete = async ( | ||||||
|     type: 'chat' | 'embedding', |     type: 'chat' | 'embedding', | ||||||
| @@ -66,146 +66,157 @@ const ModelProvider = ({ | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   const modelCount = | ||||||
|  |     modelProvider.chatModels.filter((m) => m.key !== 'error').length + | ||||||
|  |     modelProvider.embeddingModels.filter((m) => m.key !== 'error').length; | ||||||
|  |   const hasError = | ||||||
|  |     modelProvider.chatModels.some((m) => m.key === 'error') || | ||||||
|  |     modelProvider.embeddingModels.some((m) => m.key === 'error'); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div |     <div | ||||||
|       key={modelProvider.id} |       key={modelProvider.id} | ||||||
|       className="border border-light-200 dark:border-dark-200 rounded-lg overflow-hidden" |       className="border border-light-200 dark:border-dark-200 rounded-lg overflow-hidden bg-light-primary dark:bg-dark-primary" | ||||||
|     > |     > | ||||||
|       <div |       <div className="px-5 py-3.5 flex flex-row justify-between w-full items-center border-b border-light-200 dark:border-dark-200 bg-light-secondary/30 dark:bg-dark-secondary/30"> | ||||||
|         className={cn( |         <div className="flex items-center gap-2.5"> | ||||||
|           'group px-5 py-4 flex flex-row justify-between w-full cursor-pointer hover:bg-light-secondary hover:dark:bg-dark-secondary transition duration-200 items-center', |           <div className="p-1.5 rounded-md bg-sky-500/10 dark:bg-sky-500/10"> | ||||||
|           !open && 'rounded-lg', |             <Plug2 size={14} className="text-sky-500" /> | ||||||
|         )} |  | ||||||
|         onClick={() => setOpen(!open)} |  | ||||||
|       > |  | ||||||
|         <p className="text-sm lg:text-base text-black dark:text-white font-medium"> |  | ||||||
|           {modelProvider.name} |  | ||||||
|         </p> |  | ||||||
|         <div className="flex items-center gap-4"> |  | ||||||
|           <div className="flex flex-row items-center"> |  | ||||||
|             <UpdateProvider |  | ||||||
|               fields={fields} |  | ||||||
|               modelProvider={modelProvider} |  | ||||||
|               setProviders={setProviders} |  | ||||||
|             /> |  | ||||||
|             <DeleteProvider |  | ||||||
|               modelProvider={modelProvider} |  | ||||||
|               setProviders={setProviders} |  | ||||||
|             /> |  | ||||||
|           </div> |           </div> | ||||||
|           <ChevronDown |           <div className="flex flex-col"> | ||||||
|             size={16} |             <p className="text-sm lg:text-base text-black dark:text-white font-medium"> | ||||||
|             className={cn( |               {modelProvider.name} | ||||||
|               open ? 'rotate-180' : '', |             </p> | ||||||
|               'transition duration-200 text-black/70 dark:text-white/70 group-hover:text-sky-500', |             {modelCount > 0 && ( | ||||||
|  |               <p className="text-[10px] lg:text-xs text-black/50 dark:text-white/50"> | ||||||
|  |                 {modelCount} model{modelCount !== 1 ? 's' : ''} configured | ||||||
|  |               </p> | ||||||
|             )} |             )} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div className="flex flex-row items-center gap-1"> | ||||||
|  |           <UpdateProvider | ||||||
|  |             fields={fields} | ||||||
|  |             modelProvider={modelProvider} | ||||||
|  |             setProviders={setProviders} | ||||||
|  |           /> | ||||||
|  |           <DeleteProvider | ||||||
|  |             modelProvider={modelProvider} | ||||||
|  |             setProviders={setProviders} | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <AnimatePresence> |       <div className="flex flex-col gap-y-4 px-5 py-4"> | ||||||
|         {open && ( |         <div className="flex flex-col gap-y-2"> | ||||||
|           <motion.div |           <div className="flex flex-row w-full justify-between items-center"> | ||||||
|             initial={{ height: 0, opacity: 0 }} |             <p className="text-[11px] lg:text-xs font-medium text-black/70 dark:text-white/70 uppercase tracking-wide"> | ||||||
|             animate={{ height: 'auto', opacity: 1 }} |               Chat Models | ||||||
|             exit={{ height: 0, opacity: 0 }} |             </p> | ||||||
|             transition={{ duration: 0.1 }} |             {!modelProvider.chatModels.some((m) => m.key === 'error') && ( | ||||||
|           > |               <AddModel | ||||||
|             <div className="border-t border-light-200 dark:border-dark-200" /> |                 providerId={modelProvider.id} | ||||||
|             <div className="flex flex-col gap-y-4 px-5 py-4"> |                 setProviders={setProviders} | ||||||
|               <div className="flex flex-col gap-y-2"> |                 type="chat" | ||||||
|                 <div className="flex flex-row w-full justify-between items-center"> |               /> | ||||||
|                   <p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70"> |             )} | ||||||
|                     Chat models |           </div> | ||||||
|                   </p> |           <div className="flex flex-col gap-2"> | ||||||
|                   <AddModel |             {modelProvider.chatModels.some((m) => m.key === 'error') ? ( | ||||||
|                     providerId={modelProvider.id} |               <div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30"> | ||||||
|                     setProviders={setProviders} |                 <AlertCircle size={16} className="shrink-0" /> | ||||||
|                     type="chat" |                 <span className="break-words"> | ||||||
|                   /> |                   { | ||||||
|                 </div> |                     modelProvider.chatModels.find((m) => m.key === 'error') | ||||||
|                 <div className="flex flex-col gap-2"> |                       ?.name | ||||||
|                   {modelProvider.chatModels.some((m) => m.key === 'error') ? ( |                   } | ||||||
|                     <div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30"> |                 </span> | ||||||
|                       <AlertCircle size={16} className="shrink-0" /> |  | ||||||
|                       <span className="break-words"> |  | ||||||
|                         { |  | ||||||
|                           modelProvider.chatModels.find( |  | ||||||
|                             (m) => m.key === 'error', |  | ||||||
|                           )?.name |  | ||||||
|                         } |  | ||||||
|                       </span> |  | ||||||
|                     </div> |  | ||||||
|                   ) : ( |  | ||||||
|                     <div className="flex flex-row flex-wrap gap-2"> |  | ||||||
|                       {modelProvider.chatModels.map((model, index) => ( |  | ||||||
|                         <div |  | ||||||
|                           key={`${modelProvider.id}-chat-${model.key}-${index}`} |  | ||||||
|                           className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5" |  | ||||||
|                         > |  | ||||||
|                           <span>{model.name}</span> |  | ||||||
|                           <button |  | ||||||
|                             onClick={() => { |  | ||||||
|                               handleModelDelete('chat', model.key); |  | ||||||
|                             }} |  | ||||||
|                           > |  | ||||||
|                             <X size={12} /> |  | ||||||
|                           </button> |  | ||||||
|                         </div> |  | ||||||
|                       ))} |  | ||||||
|                     </div> |  | ||||||
|                   )} |  | ||||||
|                 </div> |  | ||||||
|               </div> |               </div> | ||||||
|               <div className="flex flex-col gap-y-2"> |             ) : modelProvider.chatModels.filter((m) => m.key !== 'error') | ||||||
|                 <div className="flex flex-row w-full justify-between items-center"> |                 .length === 0 && !hasError ? ( | ||||||
|                   <p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70"> |               <div className="flex flex-col items-center justify-center py-4 px-4 rounded-lg border-2 border-dashed border-light-200 dark:border-dark-200 bg-light-secondary/20 dark:bg-dark-secondary/20"> | ||||||
|                     Embedding models |                 <p className="text-xs text-black/50 dark:text-white/50 text-center"> | ||||||
|                   </p> |                   No chat models configured | ||||||
|                   <AddModel |                 </p> | ||||||
|                     providerId={modelProvider.id} |  | ||||||
|                     setProviders={setProviders} |  | ||||||
|                     type="embedding" |  | ||||||
|                   /> |  | ||||||
|                 </div> |  | ||||||
|                 <div className="flex flex-col gap-2"> |  | ||||||
|                   {modelProvider.embeddingModels.some( |  | ||||||
|                     (m) => m.key === 'error', |  | ||||||
|                   ) ? ( |  | ||||||
|                     <div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30"> |  | ||||||
|                       <AlertCircle size={16} className="shrink-0" /> |  | ||||||
|                       <span className="break-words"> |  | ||||||
|                         { |  | ||||||
|                           modelProvider.embeddingModels.find( |  | ||||||
|                             (m) => m.key === 'error', |  | ||||||
|                           )?.name |  | ||||||
|                         } |  | ||||||
|                       </span> |  | ||||||
|                     </div> |  | ||||||
|                   ) : ( |  | ||||||
|                     <div className="flex flex-row flex-wrap gap-2"> |  | ||||||
|                       {modelProvider.embeddingModels.map((model, index) => ( |  | ||||||
|                         <div |  | ||||||
|                           key={`${modelProvider.id}-embedding-${model.key}-${index}`} |  | ||||||
|                           className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5" |  | ||||||
|                         > |  | ||||||
|                           <span>{model.name}</span> |  | ||||||
|                           <button |  | ||||||
|                             onClick={() => { |  | ||||||
|                               handleModelDelete('embedding', model.key); |  | ||||||
|                             }} |  | ||||||
|                           > |  | ||||||
|                             <X size={12} /> |  | ||||||
|                           </button> |  | ||||||
|                         </div> |  | ||||||
|                       ))} |  | ||||||
|                     </div> |  | ||||||
|                   )} |  | ||||||
|                 </div> |  | ||||||
|               </div> |               </div> | ||||||
|             </div> |             ) : modelProvider.chatModels.filter((m) => m.key !== 'error') | ||||||
|           </motion.div> |                 .length > 0 ? ( | ||||||
|         )} |               <div className="flex flex-row flex-wrap gap-2"> | ||||||
|       </AnimatePresence> |                 {modelProvider.chatModels.map((model, index) => ( | ||||||
|  |                   <div | ||||||
|  |                     key={`${modelProvider.id}-chat-${model.key}-${index}`} | ||||||
|  |                     className="flex flex-row items-center space-x-1.5 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200" | ||||||
|  |                   > | ||||||
|  |                     <span>{model.name}</span> | ||||||
|  |                     <button | ||||||
|  |                       onClick={() => { | ||||||
|  |                         handleModelDelete('chat', model.key); | ||||||
|  |                       }} | ||||||
|  |                       className="hover:text-red-500 dark:hover:text-red-400 transition-colors" | ||||||
|  |                     > | ||||||
|  |                       <X size={12} /> | ||||||
|  |                     </button> | ||||||
|  |                   </div> | ||||||
|  |                 ))} | ||||||
|  |               </div> | ||||||
|  |             ) : null} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <div className="flex flex-col gap-y-2"> | ||||||
|  |           <div className="flex flex-row w-full justify-between items-center"> | ||||||
|  |             <p className="text-[11px] lg:text-xs font-medium text-black/70 dark:text-white/70 uppercase tracking-wide"> | ||||||
|  |               Embedding Models | ||||||
|  |             </p> | ||||||
|  |             {!modelProvider.embeddingModels.some((m) => m.key === 'error') && ( | ||||||
|  |               <AddModel | ||||||
|  |                 providerId={modelProvider.id} | ||||||
|  |                 setProviders={setProviders} | ||||||
|  |                 type="embedding" | ||||||
|  |               /> | ||||||
|  |             )} | ||||||
|  |           </div> | ||||||
|  |           <div className="flex flex-col gap-2"> | ||||||
|  |             {modelProvider.embeddingModels.some((m) => m.key === 'error') ? ( | ||||||
|  |               <div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30"> | ||||||
|  |                 <AlertCircle size={16} className="shrink-0" /> | ||||||
|  |                 <span className="break-words"> | ||||||
|  |                   { | ||||||
|  |                     modelProvider.embeddingModels.find((m) => m.key === 'error') | ||||||
|  |                       ?.name | ||||||
|  |                   } | ||||||
|  |                 </span> | ||||||
|  |               </div> | ||||||
|  |             ) : modelProvider.embeddingModels.filter((m) => m.key !== 'error') | ||||||
|  |                 .length === 0 && !hasError ? ( | ||||||
|  |               <div className="flex flex-col items-center justify-center py-4 px-4 rounded-lg border-2 border-dashed border-light-200 dark:border-dark-200 bg-light-secondary/20 dark:bg-dark-secondary/20"> | ||||||
|  |                 <p className="text-xs text-black/50 dark:text-white/50 text-center"> | ||||||
|  |                   No embedding models configured | ||||||
|  |                 </p> | ||||||
|  |               </div> | ||||||
|  |             ) : modelProvider.embeddingModels.filter((m) => m.key !== 'error') | ||||||
|  |                 .length > 0 ? ( | ||||||
|  |               <div className="flex flex-row flex-wrap gap-2"> | ||||||
|  |                 {modelProvider.embeddingModels.map((model, index) => ( | ||||||
|  |                   <div | ||||||
|  |                     key={`${modelProvider.id}-embedding-${model.key}-${index}`} | ||||||
|  |                     className="flex flex-row items-center space-x-1.5 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200" | ||||||
|  |                   > | ||||||
|  |                     <span>{model.name}</span> | ||||||
|  |                     <button | ||||||
|  |                       onClick={() => { | ||||||
|  |                         handleModelDelete('embedding', model.key); | ||||||
|  |                       }} | ||||||
|  |                       className="hover:text-red-500 dark:hover:text-red-400 transition-colors" | ||||||
|  |                     > | ||||||
|  |                       <X size={12} /> | ||||||
|  |                     </button> | ||||||
|  |                   </div> | ||||||
|  |                 ))} | ||||||
|  |               </div> | ||||||
|  |             ) : null} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -64,8 +64,8 @@ const ModelSelect = ({ | |||||||
|           </h4> |           </h4> | ||||||
|           <p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50"> |           <p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50"> | ||||||
|             {type === 'chat' |             {type === 'chat' | ||||||
|               ? 'Select the model to use for chat responses' |               ? 'Choose which model to use for generating responses' | ||||||
|               : 'Select the model to use for embeddings'} |               : 'Choose which model to use for generating embeddings'} | ||||||
|           </p> |           </p> | ||||||
|         </div> |         </div> | ||||||
|         <Select |         <Select | ||||||
|   | |||||||
| @@ -39,22 +39,50 @@ const Models = ({ | |||||||
|       <div className="border-t border-light-200 dark:border-dark-200" /> |       <div className="border-t border-light-200 dark:border-dark-200" /> | ||||||
|       <div className="flex flex-row justify-between items-center px-6 "> |       <div className="flex flex-row justify-between items-center px-6 "> | ||||||
|         <p className="text-xs lg:text-sm text-black/70 dark:text-white/70"> |         <p className="text-xs lg:text-sm text-black/70 dark:text-white/70"> | ||||||
|           Manage model provider |           Manage connections | ||||||
|         </p> |         </p> | ||||||
|         <AddProvider modelProviders={fields} setProviders={setProviders} /> |         <AddProvider modelProviders={fields} setProviders={setProviders} /> | ||||||
|       </div> |       </div> | ||||||
|       <div className="flex flex-col px-6 gap-y-4"> |       <div className="flex flex-col px-6 gap-y-4"> | ||||||
|         {providers.map((provider) => ( |         {providers.length === 0 ? ( | ||||||
|           <ModelProvider |           <div className="flex flex-col items-center justify-center py-12 px-4 rounded-lg border-2 border-dashed border-light-200 dark:border-dark-200 bg-light-secondary/10 dark:bg-dark-secondary/10"> | ||||||
|             key={`provider-${provider.id}`} |             <div className="p-3 rounded-full bg-sky-500/10 dark:bg-sky-500/10 mb-3"> | ||||||
|             fields={ |               <svg | ||||||
|               (fields.find((f) => f.key === provider.type)?.fields ?? |                 xmlns="http://www.w3.org/2000/svg" | ||||||
|                 []) as UIConfigField[] |                 className="w-8 h-8 text-sky-500" | ||||||
|             } |                 fill="none" | ||||||
|             modelProvider={provider} |                 viewBox="0 0 24 24" | ||||||
|             setProviders={setProviders} |                 stroke="currentColor" | ||||||
|           /> |               > | ||||||
|         ))} |                 <path | ||||||
|  |                   strokeLinecap="round" | ||||||
|  |                   strokeLinejoin="round" | ||||||
|  |                   strokeWidth={2} | ||||||
|  |                   d="M13 10V3L4 14h7v7l9-11h-7z" | ||||||
|  |                 /> | ||||||
|  |               </svg> | ||||||
|  |             </div> | ||||||
|  |             <p className="text-sm font-medium text-black/70 dark:text-white/70 mb-1"> | ||||||
|  |               No connections yet | ||||||
|  |             </p> | ||||||
|  |             <p className="text-xs text-black/50 dark:text-white/50 text-center max-w-sm mb-4"> | ||||||
|  |               Add your first connection to start using AI models. Connect to | ||||||
|  |               OpenAI, Anthropic, Ollama, and more. | ||||||
|  |             </p> | ||||||
|  |           </div> | ||||||
|  |         ) : ( | ||||||
|  |           providers.map((provider) => ( | ||||||
|  |             <ModelProvider | ||||||
|  |               key={`provider-${provider.id}`} | ||||||
|  |               fields={ | ||||||
|  |                 (fields.find((f) => f.key === provider.type)?.fields ?? | ||||||
|  |                   []) as UIConfigField[] | ||||||
|  |               } | ||||||
|  |               modelProvider={provider} | ||||||
|  |               setProviders={setProviders} | ||||||
|  |             /> | ||||||
|  |           )) | ||||||
|  |         )} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -67,10 +67,10 @@ const UpdateProvider = ({ | |||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       toast.success('Provider updated successfully.'); |       toast.success('Connection updated successfully.'); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       console.error('Error updating provider:', error); |       console.error('Error updating provider:', error); | ||||||
|       toast.error('Failed to update provider.'); |       toast.error('Failed to update connection.'); | ||||||
|     } finally { |     } finally { | ||||||
|       setLoading(false); |       setLoading(false); | ||||||
|       setOpen(false); |       setOpen(false); | ||||||
| @@ -110,7 +110,7 @@ const UpdateProvider = ({ | |||||||
|                 <form onSubmit={handleSubmit} className="flex flex-col flex-1"> |                 <form onSubmit={handleSubmit} className="flex flex-col flex-1"> | ||||||
|                   <div className="px-6 pt-6 pb-4"> |                   <div className="px-6 pt-6 pb-4"> | ||||||
|                     <h3 className="text-black/90 dark:text-white/90 font-medium"> |                     <h3 className="text-black/90 dark:text-white/90 font-medium"> | ||||||
|                       Update provider |                       Update connection | ||||||
|                     </h3> |                     </h3> | ||||||
|                   </div> |                   </div> | ||||||
|                   <div className="border-t border-light-200 dark:border-dark-200" /> |                   <div className="border-t border-light-200 dark:border-dark-200" /> | ||||||
| @@ -121,13 +121,13 @@ const UpdateProvider = ({ | |||||||
|                         className="flex flex-col items-start space-y-2" |                         className="flex flex-col items-start space-y-2" | ||||||
|                       > |                       > | ||||||
|                         <label className="text-xs text-black/70 dark:text-white/70"> |                         <label className="text-xs text-black/70 dark:text-white/70"> | ||||||
|                           Name* |                           Connection Name* | ||||||
|                         </label> |                         </label> | ||||||
|                         <input |                         <input | ||||||
|                           value={name} |                           value={name} | ||||||
|                           onChange={(event) => setName(event.target.value)} |                           onChange={(event) => setName(event.target.value)} | ||||||
|                           className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60" |                           className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60" | ||||||
|                           placeholder={'Provider Name'} |                           placeholder={'Connection Name'} | ||||||
|                           type="text" |                           type="text" | ||||||
|                           required={true} |                           required={true} | ||||||
|                         /> |                         /> | ||||||
| @@ -171,7 +171,7 @@ const UpdateProvider = ({ | |||||||
|                       {loading ? ( |                       {loading ? ( | ||||||
|                         <Loader2 className="animate-spin" size={16} /> |                         <Loader2 className="animate-spin" size={16} /> | ||||||
|                       ) : ( |                       ) : ( | ||||||
|                         'Update Provider' |                         'Update Connection' | ||||||
|                       )} |                       )} | ||||||
|                     </button> |                     </button> | ||||||
|                   </div> |                   </div> | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ const sections = [ | |||||||
|   { |   { | ||||||
|     key: 'models', |     key: 'models', | ||||||
|     name: 'Models', |     name: 'Models', | ||||||
|     description: 'Configure model settings.', |     description: 'Connect to AI services and manage connections.', | ||||||
|     icon: BrainCog, |     icon: BrainCog, | ||||||
|     component: Models, |     component: Models, | ||||||
|     dataAdd: 'modelProviders', |     dataAdd: 'modelProviders', | ||||||
|   | |||||||
| @@ -63,7 +63,11 @@ const SetupConfig = ({ | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const hasProviders = providers.length > 0; |   const visibleProviders = providers.filter( | ||||||
|  |     (p) => p.name.toLowerCase() !== 'transformers', | ||||||
|  |   ); | ||||||
|  |   const hasProviders = | ||||||
|  |     visibleProviders.filter((p) => p.chatModels.length > 0).length > 0; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="w-[95vw] md:w-[80vw] lg:w-[65vw] mx-auto px-2 sm:px-4 md:px-6 flex flex-col space-y-6"> |     <div className="w-[95vw] md:w-[80vw] lg:w-[65vw] mx-auto px-2 sm:px-4 md:px-6 flex flex-col space-y-6"> | ||||||
| @@ -81,10 +85,10 @@ const SetupConfig = ({ | |||||||
|             <div className="flex flex-row justify-between items-center mb-4 md:mb-6 pb-3 md:pb-4 border-b border-light-200 dark:border-dark-200"> |             <div className="flex flex-row justify-between items-center mb-4 md:mb-6 pb-3 md:pb-4 border-b border-light-200 dark:border-dark-200"> | ||||||
|               <div> |               <div> | ||||||
|                 <p className="text-xs sm:text-sm font-medium text-black dark:text-white"> |                 <p className="text-xs sm:text-sm font-medium text-black dark:text-white"> | ||||||
|                   Manage Providers |                   Manage Connections | ||||||
|                 </p> |                 </p> | ||||||
|                 <p className="text-[10px] sm:text-xs text-black/50 dark:text-white/50 mt-0.5"> |                 <p className="text-[10px] sm:text-xs text-black/50 dark:text-white/50 mt-0.5"> | ||||||
|                   Add and configure your model providers |                   Add connections to access AI models | ||||||
|                 </p> |                 </p> | ||||||
|               </div> |               </div> | ||||||
|               <AddProvider |               <AddProvider | ||||||
| @@ -100,14 +104,17 @@ const SetupConfig = ({ | |||||||
|                     Loading providers... |                     Loading providers... | ||||||
|                   </p> |                   </p> | ||||||
|                 </div> |                 </div> | ||||||
|               ) : providers.length === 0 ? ( |               ) : visibleProviders.length === 0 ? ( | ||||||
|                 <div className="flex flex-col items-center justify-center py-8 md:py-12 text-center"> |                 <div className="flex flex-col items-center justify-center py-8 md:py-12 text-center"> | ||||||
|                   <p className="text-xs sm:text-sm font-medium text-black/70 dark:text-white/70"> |                   <p className="text-xs sm:text-sm font-medium text-black/70 dark:text-white/70"> | ||||||
|                     No providers configured |                     No connections configured | ||||||
|  |                   </p> | ||||||
|  |                   <p className="text-[10px] sm:text-xs text-black/50 dark:text-white/50 mt-1"> | ||||||
|  |                     Click "Add Connection" above to get started | ||||||
|                   </p> |                   </p> | ||||||
|                 </div> |                 </div> | ||||||
|               ) : ( |               ) : ( | ||||||
|                 providers.map((provider) => ( |                 visibleProviders.map((provider) => ( | ||||||
|                   <ModelProvider |                   <ModelProvider | ||||||
|                     key={`provider-${provider.id}`} |                     key={`provider-${provider.id}`} | ||||||
|                     fields={ |                     fields={ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user