feat(agents): add api server check and warning for agent features

- Add api server enabled check in multiple components
- Show warning alert when api server is disabled
- Add dismissable warning in AgentSection
- Disable send button when api server is disabled
- Add iknow state to store dismissed warnings
This commit is contained in:
icarus 2025-09-22 17:48:05 +08:00
parent 9425437480
commit 3c12f9052e
8 changed files with 76 additions and 12 deletions

View File

@ -1,3 +1,4 @@
import { Alert } from '@heroui/react'
import { loggerService } from '@logger'
import { ContentSearch, ContentSearchRef } from '@renderer/components/ContentSearch'
import { HStack } from '@renderer/components/Layout'
@ -49,6 +50,7 @@ const Chat: FC<Props> = (props) => {
const chatMaxWidth = useChatMaxWidth()
const { chat } = useRuntime()
const { activeTopicOrSession, activeAgentId, activeSessionId } = chat
const { apiServer } = useSettings()
const mainRef = React.useRef<HTMLDivElement>(null)
const contentSearchRef = React.useRef<ContentSearchRef>(null)
@ -149,8 +151,15 @@ const Chat: FC<Props> = (props) => {
if (!sessionId) {
return () => <div> Active Session ID is invalid.</div>
}
if (!apiServer.enabled) {
return () => (
<div>
<Alert color="warning" title={t('agent.warning.enable_server')} />
</div>
)
}
return () => <AgentSessionMessages agentId={activeAgentId} sessionId={sessionId} />
}, [activeAgentId, activeSessionId])
}, [activeAgentId, activeSessionId, apiServer.enabled, t])
const SessionInputBar = useMemo(() => {
if (activeAgentId === null) {

View File

@ -36,6 +36,7 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
const [text, setText] = useState(_text)
const [inputFocus, setInputFocus] = useState(false)
const { session } = useSession(agentId, sessionId)
const { apiServer } = useSettings()
const { sendMessageShortcut, fontSize, enableSpellCheck } = useSettings()
const textareaRef = useRef<TextAreaRef>(null)
@ -52,6 +53,7 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
}, [])
const inputEmpty = isEmpty(text)
const sendDisabled = inputEmpty || !apiServer.enabled
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
//to check if the SendMessage key is pressed
@ -96,7 +98,7 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
}
const sendMessage = useCallback(async () => {
if (inputEmpty) {
if (sendDisabled) {
return
}
@ -158,7 +160,7 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
}, [
agentId,
dispatch,
inputEmpty,
sendDisabled,
session?.agent_id,
session?.instructions,
session?.model,
@ -231,7 +233,7 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
onBlur={() => setInputFocus(false)}
/>
<div className="flex justify-end px-1">
<SendMessageButton sendMessage={sendMessage} disabled={inputEmpty} />
<SendMessageButton sendMessage={sendMessage} disabled={sendDisabled} />
</div>
</InputBarContainer>
</Container>

View File

@ -3,7 +3,7 @@ import { Assistant } from '@renderer/types'
import { FC, useRef } from 'react'
import styled from 'styled-components'
import { Agents } from './components/Agents'
import { AgentSection } from './components/AgentSection'
import Assistants from './components/Assistants'
interface AssistantsTabProps {
@ -17,7 +17,7 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
const containerRef = useRef<HTMLDivElement>(null)
return (
<Container className="assistants-tab" ref={containerRef}>
<Agents />
<AgentSection />
<Assistants {...props} />
</Container>
)

View File

@ -1,5 +1,6 @@
import { Spinner } from '@heroui/react'
import { Alert, Spinner } from '@heroui/react'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import { AnimatePresence, motion } from 'framer-motion'
import { FC, memo } from 'react'
import { useTranslation } from 'react-i18next'
@ -12,6 +13,15 @@ const SessionsTab: FC<SessionsTabProps> = () => {
const { chat } = useRuntime()
const { activeAgentId } = chat
const { t } = useTranslation()
const { apiServer } = useSettings()
if (!apiServer.enabled) {
return (
<div>
<Alert color="warning" title={t('agent.warning.enable_server')} />
</div>
)
}
return (
<AnimatePresence mode="wait">

View File

@ -0,0 +1,39 @@
import { Alert } from '@heroui/react'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store'
import { addIknowAction } from '@renderer/store/runtime'
import { useTranslation } from 'react-i18next'
import { Agents } from './Agents'
import { SectionName } from './SectionName'
const ALERT_KEY = 'enable_api_server_to_use_agent'
export const AgentSection = () => {
const { t } = useTranslation()
const { apiServer } = useSettings()
const { iknow } = useRuntime()
const dispatch = useAppDispatch()
if (!apiServer.enabled) {
if (iknow[ALERT_KEY]) return null
return (
<Alert
color="warning"
title={t('agent.warning.enable_server')}
isClosable
onClose={() => {
dispatch(addIknowAction(ALERT_KEY))
}}
/>
)
}
return (
<div className="agents-tab h-full w-full">
<SectionName name={t('common.agent_other')} />
<Agents />
</div>
)
}

View File

@ -9,7 +9,6 @@ import { FC, useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import AgentItem from './AgentItem'
import { SectionName } from './SectionName'
interface AssistantsTabProps {}
@ -35,8 +34,7 @@ export const Agents: FC<AssistantsTabProps> = () => {
}, [isLoading, agents, activeAgentId, setActiveAgentId])
return (
<div className="agents-tab h-full w-full">
<SectionName name={t('common.agent_other')} />
<>
{isLoading && <Spinner />}
{error && <Alert color="danger" title={t('agent.list.error.failed')} />}
{!isLoading &&
@ -65,6 +63,6 @@ export const Agents: FC<AssistantsTabProps> = () => {
)
}}
/>
</div>
</>
)
}

View File

@ -53,6 +53,7 @@ export interface RuntimeState {
export: ExportState
chat: ChatState
websearch: WebSearchState
iknow: Record<string, boolean>
}
export interface ExportState {
@ -93,7 +94,8 @@ const initialState: RuntimeState = {
},
websearch: {
activeSearches: {}
}
},
iknow: {}
}
const runtimeSlice = createSlice({
@ -179,6 +181,9 @@ const runtimeSlice = createSlice({
delete state.websearch.activeSearches[requestId]
}
state.websearch.activeSearches[requestId] = status
},
addIknowAction: (state, action: PayloadAction<string>) => {
state.iknow[action.payload] = true
}
}
})
@ -197,6 +202,7 @@ export const {
setResourcesPath,
setUpdateState,
setExportState,
addIknowAction,
// Chat related actions
toggleMultiSelectMode,
setSelectedMessageIds,