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