diff --git a/site/src/pages/AgentsPage/AgentChatPage.tsx b/site/src/pages/AgentsPage/AgentChatPage.tsx
index 358355adbef66..eb9fd221c6980 100644
--- a/site/src/pages/AgentsPage/AgentChatPage.tsx
+++ b/site/src/pages/AgentsPage/AgentChatPage.tsx
@@ -63,7 +63,7 @@ import {
} from "./AgentChatPageView";
import type { AgentsOutletContext } from "./AgentsPage";
import type { ChatMessageInputRef } from "./components/AgentChatInput";
-import { AgentSetupNotice } from "./components/AgentSetupNotice";
+import { getAgentSetupNoticeConfig } from "./components/AgentSetupNoticeBanner";
import { normalizeChatErrorPayload } from "./components/ChatConversation/chatError";
import {
getParentChatID,
@@ -1129,31 +1129,11 @@ const AgentChatPage: FC = () => {
hasConfiguredModels,
hasUserFixableModelProviders,
});
- const isAdmin = permissions.editDeploymentConfig;
- const agentSetupNotice = (() => {
- // Admin: show when providers or models are missing
- if (
- isAdmin &&
- providerCount !== undefined &&
- modelCount !== undefined &&
- (providerCount === 0 || modelCount === 0)
- ) {
- return (
-
- );
- }
- // Member: show when no models are available
- if (!isAdmin && modelCount !== undefined && modelCount === 0) {
- return (
-
- );
- }
- return undefined;
- })();
+ const agentSetupNotice = getAgentSetupNoticeConfig({
+ canConfigureAgentSetup: permissions.editDeploymentConfig,
+ providerCount: providerCount ?? 0,
+ modelCount: modelCount ?? 0,
+ });
const isSubmissionPending =
isSendPending || isEditPending || isInterruptPending;
const isChatSettingsPending =
diff --git a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx
index 58bf4ec44d0f6..71d98908f4549 100644
--- a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx
+++ b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx
@@ -25,7 +25,6 @@ import {
AgentChatPageNotFoundView,
AgentChatPageView,
} from "./AgentChatPageView";
-import { AgentSetupNotice } from "./components/AgentSetupNotice";
import {
createChatStore,
useChatSelector,
@@ -526,9 +525,11 @@ export const NoModelOptions: Story = {
export const MissingProviderAndModelSetup: Story = {
render: () => (
- }
+ agentSetupNotice={{
+ canConfigureAgentSetup: true,
+ providerCount: 0,
+ modelCount: 0,
+ }}
hasModelOptions={false}
modelOptions={[]}
isInputDisabled
@@ -561,9 +562,11 @@ export const MissingProviderAndModelSetup: Story = {
export const MissingModelSetup: Story = {
render: () => (
- }
+ agentSetupNotice={{
+ canConfigureAgentSetup: true,
+ providerCount: 1,
+ modelCount: 0,
+ }}
hasModelOptions={false}
modelOptions={[]}
isInputDisabled
@@ -592,9 +595,11 @@ export const MissingModelSetup: Story = {
export const MissingProviderSetup: Story = {
render: () => (
- }
+ agentSetupNotice={{
+ canConfigureAgentSetup: true,
+ providerCount: 0,
+ modelCount: 1,
+ }}
/>
),
play: async ({ canvasElement }) => {
@@ -620,9 +625,11 @@ export const MissingProviderSetup: Story = {
export const MemberNoModelsAvailable: Story = {
render: () => (
- }
+ agentSetupNotice={{
+ canConfigureAgentSetup: false,
+ providerCount: 0,
+ modelCount: 0,
+ }}
hasModelOptions={false}
modelOptions={[]}
isInputDisabled
diff --git a/site/src/pages/AgentsPage/AgentChatPageView.tsx b/site/src/pages/AgentsPage/AgentChatPageView.tsx
index c6cdd3c381518..e120bb1c1ff5e 100644
--- a/site/src/pages/AgentsPage/AgentChatPageView.tsx
+++ b/site/src/pages/AgentsPage/AgentChatPageView.tsx
@@ -22,6 +22,7 @@ import {
AgentChatInput,
type ChatMessageInputRef,
} from "./components/AgentChatInput";
+import type { AgentSetupNoticeConfig } from "./components/AgentSetupNoticeBanner";
import {
ChatConversationSkeleton,
RightPanelSkeleton,
@@ -116,7 +117,7 @@ interface AgentChatPageViewProps {
modelOptions: readonly ModelSelectorOption[];
modelSelectorPlaceholder: string;
modelSelectorHelp?: ReactNode;
- agentSetupNotice?: ReactNode;
+ agentSetupNotice?: AgentSetupNoticeConfig;
hasModelOptions: boolean;
isModelCatalogLoading?: boolean;
planModeEnabled?: boolean;
diff --git a/site/src/pages/AgentsPage/AgentCreatePage.tsx b/site/src/pages/AgentsPage/AgentCreatePage.tsx
index 18415a820b4bf..26bb381bc071b 100644
--- a/site/src/pages/AgentsPage/AgentCreatePage.tsx
+++ b/site/src/pages/AgentsPage/AgentCreatePage.tsx
@@ -21,7 +21,7 @@ import {
type CreateChatOptions,
} from "./components/AgentCreateForm";
import { AgentPageHeader } from "./components/AgentPageHeader";
-import { AgentSetupNotice } from "./components/AgentSetupNotice";
+import { getAgentSetupNoticeConfig } from "./components/AgentSetupNoticeBanner";
import { ChimeButton } from "./components/ChimeButton";
import { WebPushButton } from "./components/WebPushButton";
import { getAgentChatSendShortcut } from "./utils/agentChatSendShortcut";
@@ -72,29 +72,11 @@ const AgentCreatePage: FC = () => {
chatModelConfigsQuery.isSuccess && chatModelsQuery.isSuccess
? catalogModelOptions.length
: undefined;
- const isAdmin = permissions.editDeploymentConfig;
- const agentSetupNotice = (() => {
- if (
- isAdmin &&
- providerCount !== undefined &&
- modelCount !== undefined &&
- (providerCount === 0 || modelCount === 0)
- ) {
- return (
-
- );
- }
- if (!isAdmin && modelCount !== undefined && modelCount === 0) {
- return (
-
- );
- }
- return undefined;
- })();
+ const agentSetupNotice = getAgentSetupNoticeConfig({
+ canConfigureAgentSetup: permissions.editDeploymentConfig,
+ providerCount: providerCount ?? 0,
+ modelCount: modelCount ?? 0,
+ });
const handleCreateChat = async ({
message,
diff --git a/site/src/pages/AgentsPage/components/AgentChatInput.tsx b/site/src/pages/AgentsPage/components/AgentChatInput.tsx
index b24ce61bd1198..ebcba1e1e00d6 100644
--- a/site/src/pages/AgentsPage/components/AgentChatInput.tsx
+++ b/site/src/pages/AgentsPage/components/AgentChatInput.tsx
@@ -67,6 +67,10 @@ import {
isChatAttachmentFile,
} from "../utils/chatAttachments";
import { formatProviderLabel } from "../utils/modelOptions";
+import {
+ AgentSetupNoticeBanner,
+ type AgentSetupNoticeConfig,
+} from "./AgentSetupNoticeBanner";
import {
AttachmentPreview,
isUploadInProgress,
@@ -183,7 +187,7 @@ interface AgentChatInputProps {
sshCommand?: string;
attachedWorkspace?: AttachedWorkspaceInfo;
folder?: string;
- agentSetupNotice?: React.ReactNode;
+ agentSetupNotice?: AgentSetupNoticeConfig;
}
export interface AttachedWorkspaceInfo {
@@ -1045,7 +1049,9 @@ export const AgentChatInput: FC = ({
/>
)}
{agentSetupNotice && (
- {agentSetupNotice}
+
)}
- ),
+ agentSetupNotice: {
+ canConfigureAgentSetup: true,
+ providerCount: 0,
+ modelCount: 0,
+ },
modelCatalog: { providers: [] },
modelOptions: [],
isModelCatalogLoading: false,
diff --git a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx
index d4ec3b59563fc..93462c212533f 100644
--- a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx
+++ b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx
@@ -1,11 +1,4 @@
-import {
- type FC,
- type ReactNode,
- useEffect,
- useEffectEvent,
- useRef,
- useState,
-} from "react";
+import { type FC, useEffect, useEffectEvent, useRef, useState } from "react";
import { useQuery } from "react-query";
import { Link } from "react-router";
import { toast } from "sonner";
@@ -32,6 +25,7 @@ import {
isChatUsageLimitExceededResponse,
} from "../utils/usageLimitMessage";
import { AgentChatInput } from "./AgentChatInput";
+import type { AgentSetupNoticeConfig } from "./AgentSetupNoticeBanner";
import { ChatAccessDeniedAlert } from "./ChatAccessDeniedAlert";
import type { ModelSelectorOption } from "./ChatElements";
import { CompactOrgSelector } from "./ChatElements";
@@ -132,7 +126,7 @@ interface AgentCreateFormProps {
canCreateChat: boolean;
modelCatalog: TypesGen.ChatModelsResponse | null | undefined;
modelOptions: readonly ChatModelOption[];
- agentSetupNotice?: ReactNode;
+ agentSetupNotice?: AgentSetupNoticeConfig;
isModelCatalogLoading: boolean;
modelConfigs: readonly TypesGen.ChatModelConfig[];
isModelConfigsLoading: boolean;
diff --git a/site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx b/site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx
new file mode 100644
index 0000000000000..bd641ec467e49
--- /dev/null
+++ b/site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx
@@ -0,0 +1,55 @@
+import { AgentSetupNotice } from "./AgentSetupNotice";
+
+export interface AgentSetupNoticeConfig {
+ canConfigureAgentSetup: boolean;
+ providerCount: number;
+ modelCount: number;
+}
+
+const shouldRenderAgentSetupNotice = ({
+ canConfigureAgentSetup,
+ providerCount,
+ modelCount,
+}: AgentSetupNoticeConfig) => {
+ if (!canConfigureAgentSetup) {
+ return modelCount === 0;
+ }
+
+ return providerCount === 0 || modelCount === 0;
+};
+
+export const getAgentSetupNoticeConfig = (
+ config: AgentSetupNoticeConfig,
+): AgentSetupNoticeConfig | undefined => {
+ return shouldRenderAgentSetupNotice(config) ? config : undefined;
+};
+
+export const AgentSetupNoticeBanner = ({
+ canConfigureAgentSetup,
+ providerCount,
+ modelCount,
+}: AgentSetupNoticeConfig) => {
+ if (
+ !shouldRenderAgentSetupNotice({
+ canConfigureAgentSetup,
+ providerCount,
+ modelCount,
+ })
+ ) {
+ return null;
+ }
+
+ if (!canConfigureAgentSetup) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/site/src/pages/AgentsPage/components/ChatPageContent.tsx b/site/src/pages/AgentsPage/components/ChatPageContent.tsx
index 9599b273d16f9..935349371fa1b 100644
--- a/site/src/pages/AgentsPage/components/ChatPageContent.tsx
+++ b/site/src/pages/AgentsPage/components/ChatPageContent.tsx
@@ -19,6 +19,7 @@ import {
isUploadInProgress,
type UploadState,
} from "./AgentChatInput";
+import type { AgentSetupNoticeConfig } from "./AgentSetupNoticeBanner";
import { ConversationTimeline } from "./ChatConversation/ConversationTimeline";
import { getLatestContextUsage } from "./ChatConversation/chatHelpers";
import {
@@ -169,7 +170,7 @@ interface ChatPageInputProps {
modelOptions: readonly ModelSelectorOption[];
modelSelectorPlaceholder: string;
modelSelectorHelp?: ReactNode;
- agentSetupNotice?: ReactNode;
+ agentSetupNotice?: AgentSetupNoticeConfig;
planModeEnabled?: boolean;
onPlanModeToggle?: (enabled: boolean) => void;
isModelCatalogLoading?: boolean;