diff --git a/ui/admin/app/components/agent/FirstModelProviderBanner.tsx b/ui/admin/app/components/agent/FirstModelProviderBanner.tsx deleted file mode 100644 index 26cdae823..000000000 --- a/ui/admin/app/components/agent/FirstModelProviderBanner.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Link } from "@remix-run/react"; -import { $path } from "remix-routes"; - -import { TypographyH3, TypographyP } from "~/components/Typography"; -import { ObotLogo } from "~/components/branding/ObotLogo"; -import { Button } from "~/components/ui/button"; - -export function FirstModelProviderBanner() { - return ( -
-
-
- -
- - Ready to create your first Agent? - - - You're almost there! To start creating or using{" "} - agents, you'll need access to an LLM (Large - Language Model) Model Provider. Luckily, we - support a variety of providers to help get you - started. - - -
-
-
-
- ); -} diff --git a/ui/admin/app/components/chat/Chatbar.tsx b/ui/admin/app/components/chat/Chatbar.tsx index f1c890282..b7a18120c 100644 --- a/ui/admin/app/components/chat/Chatbar.tsx +++ b/ui/admin/app/components/chat/Chatbar.tsx @@ -5,9 +5,11 @@ import { cn } from "~/lib/utils"; import { ChatActions } from "~/components/chat/ChatActions"; import { useChat } from "~/components/chat/ChatContext"; +import { ModelProviderTooltip } from "~/components/model-providers/ModelProviderTooltip"; import { LoadingSpinner } from "~/components/ui/LoadingSpinner"; import { Button } from "~/components/ui/button"; import { AutosizeTextarea } from "~/components/ui/textarea"; +import { useModelProviders } from "~/hooks/model-providers/useModelProviders"; type ChatbarProps = { className?: string; @@ -17,6 +19,7 @@ export function Chatbar({ className }: ChatbarProps) { const [input, setInput] = useState(""); const { abortRunningThread, processUserMessage, isRunning, isInvoking } = useChat(); + const { configured: modelProviderConfigured } = useModelProviders(); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -54,22 +57,29 @@ export function Chatbar({ className }: ChatbarProps) { placeholder="Type your message..." bottomContent={
- - + +
} diff --git a/ui/admin/app/components/chat/RunWorkflow.tsx b/ui/admin/app/components/chat/RunWorkflow.tsx index 4c9f2961c..be7a15054 100644 --- a/ui/admin/app/components/chat/RunWorkflow.tsx +++ b/ui/admin/app/components/chat/RunWorkflow.tsx @@ -5,12 +5,14 @@ import { WorkflowService } from "~/lib/service/api/workflowService"; import { cn } from "~/lib/utils"; import { RunWorkflowForm } from "~/components/chat/RunWorkflowForm"; +import { ModelProviderTooltip } from "~/components/model-providers/ModelProviderTooltip"; import { Button, ButtonProps } from "~/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger, } from "~/components/ui/popover"; +import { useModelProviders } from "~/hooks/model-providers/useModelProviders"; type RunWorkflowProps = { onSubmit: (params?: Record) => void; @@ -25,7 +27,7 @@ export function RunWorkflow({ ...props }: RunWorkflowProps & ButtonProps) { const [open, setOpen] = useState(false); - + const { configured: modelProviderConfigured } = useModelProviders(); const { data: workflow, isLoading } = useSWR( WorkflowService.getWorkflowById.key(workflowId), ({ workflowId }) => WorkflowService.getWorkflowById(workflowId) @@ -33,16 +35,20 @@ export function RunWorkflow({ const params = workflow?.params; - if (!params || isLoading) + if (!params || isLoading || !modelProviderConfigured) return ( - + + + ); return ( diff --git a/ui/admin/app/components/composed/FirstModelProviderBanner.tsx b/ui/admin/app/components/composed/FirstModelProviderBanner.tsx new file mode 100644 index 000000000..476afa0e4 --- /dev/null +++ b/ui/admin/app/components/composed/FirstModelProviderBanner.tsx @@ -0,0 +1,49 @@ +import { Link, useLocation } from "@remix-run/react"; +import { $path } from "remix-routes"; + +import { assetUrl } from "~/lib/utils"; + +import { TypographyH3, TypographyP } from "~/components/Typography"; +import { Button } from "~/components/ui/button"; +import { useModelProviders } from "~/hooks/model-providers/useModelProviders"; + +export function FirstModelProviderBanner() { + const { configured: modelProviderConfigured } = useModelProviders(); + const location = useLocation(); + const isModelsProviderPage = location.pathname.includes("/model-providers"); + + return isModelsProviderPage || modelProviderConfigured ? null : ( +
+
+
+
+ Obot Alert +
+
+ + Wait! You need to set up a Model Provider! + + + You're almost there! To start creating or using{" "} + Obot's features, you'll need access to an + LLM (Large Language Model) Model Provider. + Luckily, we support a variety of providers to help + get you started. + + +
+
+
+
+ ); +} diff --git a/ui/admin/app/components/model-providers/ModelProviderTooltip.tsx b/ui/admin/app/components/model-providers/ModelProviderTooltip.tsx new file mode 100644 index 000000000..a907fa420 --- /dev/null +++ b/ui/admin/app/components/model-providers/ModelProviderTooltip.tsx @@ -0,0 +1,26 @@ +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "~/components/ui/tooltip"; + +export function ModelProviderTooltip({ + children, + enabled, +}: { + children: React.ReactNode; + enabled: boolean; +}) { + return enabled ? ( + children + ) : ( + + + {children} + + + Set up a model provider to enable this feature. + + + ); +} diff --git a/ui/admin/app/components/ui/button.tsx b/ui/admin/app/components/ui/button.tsx index 6cc017ced..bd455b296 100644 --- a/ui/admin/app/components/ui/button.tsx +++ b/ui/admin/app/components/ui/button.tsx @@ -21,6 +21,8 @@ const buttonVariants = cva( ghost: "hover:bg-secondary hover:text-secondary-foreground", accent: "bg-accent text-accent-foreground shadow-sm hover:bg-accent/80", link: "text-primary hover:text-primary/70 underline-offset-4 hover:underline shadow-none hover:shadow-none", + warning: + "bg-warning text-primary-foreground shadow-sm hover:bg-warning/80", }, size: { none: "", diff --git a/ui/admin/app/hooks/model-providers/useModelProviders.tsx b/ui/admin/app/hooks/model-providers/useModelProviders.tsx new file mode 100644 index 000000000..cc55faf8f --- /dev/null +++ b/ui/admin/app/hooks/model-providers/useModelProviders.tsx @@ -0,0 +1,15 @@ +import useSWR from "swr"; + +import { ModelProviderApiService } from "~/lib/service/api/modelProviderApiService"; + +export function useModelProviders() { + const { data: modelProviders } = useSWR( + ModelProviderApiService.getModelProviders.key(), + () => ModelProviderApiService.getModelProviders() + ); + const configured = + modelProviders?.some((modelProvider) => modelProvider.configured) ?? + false; + + return { configured, modelProviders: modelProviders ?? [] }; +} diff --git a/ui/admin/app/routes/_auth.agents._index.tsx b/ui/admin/app/routes/_auth.agents._index.tsx index cebc80054..477362522 100644 --- a/ui/admin/app/routes/_auth.agents._index.tsx +++ b/ui/admin/app/routes/_auth.agents._index.tsx @@ -8,14 +8,12 @@ import useSWR, { mutate, preload } from "swr"; import { Agent } from "~/lib/model/agents"; import { AgentService } from "~/lib/service/api/agentService"; -import { ModelProviderApiService } from "~/lib/service/api/modelProviderApiService"; import { ThreadsService } from "~/lib/service/api/threadsService"; import { generateRandomName } from "~/lib/service/nameGenerator"; import { timeSince } from "~/lib/utils"; import { TypographyH2, TypographyP } from "~/components/Typography"; import { DeleteAgent } from "~/components/agent/DeleteAgent"; -import { FirstModelProviderBanner } from "~/components/agent/FirstModelProviderBanner"; import { DataTable } from "~/components/composed/DataTable"; import { Button } from "~/components/ui/button"; import { Link } from "~/components/ui/link"; @@ -30,10 +28,6 @@ export async function clientLoader() { await Promise.all([ preload(AgentService.getAgents.key(), AgentService.getAgents), preload(ThreadsService.getThreads.key(), ThreadsService.getThreads), - preload( - ModelProviderApiService.getModelProviders.key(), - ModelProviderApiService.getModelProviders - ), ]); return null; } @@ -44,11 +38,6 @@ export default function Agents() { ThreadsService.getThreads() ); - const { data: modelProviders } = useSWR( - ModelProviderApiService.getModelProviders.key(), - () => ModelProviderApiService.getModelProviders() - ); - const threadCounts = useMemo(() => { if (!getThreads.data) return {}; return getThreads.data.reduce( @@ -67,13 +56,9 @@ export default function Agents() { ); const agents = getAgents.data || []; - const modelProviderConfigured = modelProviders?.some( - (modelProvider) => modelProvider.configured - ); return (
- {modelProviderConfigured ? null : }
@@ -81,7 +66,6 @@ export default function Agents() {
diff --git a/ui/admin/app/routes/_auth.tsx b/ui/admin/app/routes/_auth.tsx index 195b0f54d..f0a750b90 100644 --- a/ui/admin/app/routes/_auth.tsx +++ b/ui/admin/app/routes/_auth.tsx @@ -2,18 +2,25 @@ import { Outlet, isRouteErrorResponse, useRouteError } from "@remix-run/react"; import { preload } from "swr"; import { ForbiddenError, UnauthorizedError } from "~/lib/service/api/apiErrors"; +import { ModelProviderApiService } from "~/lib/service/api/modelProviderApiService"; import { UserService } from "~/lib/service/api/userService"; import { useAuth } from "~/components/auth/AuthContext"; +import { FirstModelProviderBanner } from "~/components/composed/FirstModelProviderBanner"; import { Error, RouteError, Unauthorized } from "~/components/errors"; import { HeaderNav } from "~/components/header/HeaderNav"; import { Sidebar } from "~/components/sidebar"; import { SignIn } from "~/components/signin/SignIn"; export async function clientLoader() { - const me = await preload(UserService.getMe.key(), () => - UserService.getMe() - ); + const promises = await Promise.all([ + preload(UserService.getMe.key(), () => UserService.getMe()), + preload( + ModelProviderApiService.getModelProviders.key(), + ModelProviderApiService.getModelProviders + ), + ]); + const me = promises[0]; return { me }; } @@ -24,6 +31,7 @@ export default function AuthLayout() {
+
diff --git a/ui/admin/public/logo/obot-icon-surprised-yellow.svg b/ui/admin/public/logo/obot-icon-surprised-yellow.svg new file mode 100644 index 000000000..d23286cde --- /dev/null +++ b/ui/admin/public/logo/obot-icon-surprised-yellow.svg @@ -0,0 +1,18 @@ + + + +