mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 14:41:24 +08:00
fix: activate assistant/agent when creating new (#11009)
* refactor: remove unused SWITCH_ASSISTANT event and related code Clean up unused event and associated listener in HomePage component * feat(agents): improve agent handling and state management - Return result from useUpdateAgent hook - Update useActiveTopic to handle null assistantId - Add state management for active agent and topic in Tabs - Implement afterSubmit callback in AgentModal - Refactor agent press handling in AssistantsTab - Clean up HomePage state management logic - Add afterCreate callback in UnifiedAddButton * refactor(agent): update agent and session update functions to return entities Modify update functions in useUpdateAgent and useUpdateSession hooks to return updated entities. Update related components to handle the new return types and adjust type definitions accordingly. * refactor(hooks): simplify active topic hook by using useAssistant * refactor(agent): consolidate agent update types and functions Move UpdateAgentBaseOptions and related function types from hooks/agents/types.ts to types/agent.ts Update components to use new UpdateAgentFunctionUnion type Simplify component props by removing redundant type definitions * refactor(agent): update type for plugin settings update function * refactor(AgentSettings): simplify tooling settings type definitions Remove unused hooks and use direct type imports instead of ReturnType
This commit is contained in:
parent
d792bf7fe0
commit
c1fd23742f
@ -67,6 +67,7 @@ type Props = {
|
|||||||
agent?: AgentWithTools
|
agent?: AgentWithTools
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
afterSubmit?: (a: AgentEntity) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +79,7 @@ type Props = {
|
|||||||
* @param onClose - Optional callback when modal closes. From useDisclosure.
|
* @param onClose - Optional callback when modal closes. From useDisclosure.
|
||||||
* @returns Modal component for agent creation/editing
|
* @returns Modal component for agent creation/editing
|
||||||
*/
|
*/
|
||||||
export const AgentModal: React.FC<Props> = ({ agent, isOpen: _isOpen, onClose: _onClose }) => {
|
export const AgentModal: React.FC<Props> = ({ agent, isOpen: _isOpen, onClose: _onClose, afterSubmit }) => {
|
||||||
const { isOpen, onClose } = useDisclosure({ isOpen: _isOpen, onClose: _onClose })
|
const { isOpen, onClose } = useDisclosure({ isOpen: _isOpen, onClose: _onClose })
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const loadingRef = useRef(false)
|
const loadingRef = useRef(false)
|
||||||
@ -301,8 +302,13 @@ export const AgentModal: React.FC<Props> = ({ agent, isOpen: _isOpen, onClose: _
|
|||||||
configuration: form.configuration ? { ...form.configuration } : undefined
|
configuration: form.configuration ? { ...form.configuration } : undefined
|
||||||
} satisfies UpdateAgentForm
|
} satisfies UpdateAgentForm
|
||||||
|
|
||||||
updateAgent(updatePayload)
|
const result = await updateAgent(updatePayload)
|
||||||
logger.debug('Updated agent', updatePayload)
|
if (result) {
|
||||||
|
logger.debug('Updated agent', result)
|
||||||
|
afterSubmit?.(result)
|
||||||
|
} else {
|
||||||
|
logger.error('Update failed.')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const newAgent = {
|
const newAgent = {
|
||||||
type: form.type,
|
type: form.type,
|
||||||
@ -315,12 +321,13 @@ export const AgentModal: React.FC<Props> = ({ agent, isOpen: _isOpen, onClose: _
|
|||||||
configuration: form.configuration ? { ...form.configuration } : undefined
|
configuration: form.configuration ? { ...form.configuration } : undefined
|
||||||
} satisfies AddAgentForm
|
} satisfies AddAgentForm
|
||||||
const result = await addAgent(newAgent)
|
const result = await addAgent(newAgent)
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
loadingRef.current = false
|
loadingRef.current = false
|
||||||
throw result.error
|
throw result.error
|
||||||
}
|
}
|
||||||
|
afterSubmit?.(result.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingRef.current = false
|
loadingRef.current = false
|
||||||
|
|
||||||
// setTimeoutTimer('onCreateAgent', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
|
// setTimeoutTimer('onCreateAgent', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
|
||||||
@ -329,16 +336,17 @@ export const AgentModal: React.FC<Props> = ({ agent, isOpen: _isOpen, onClose: _
|
|||||||
[
|
[
|
||||||
form.type,
|
form.type,
|
||||||
form.model,
|
form.model,
|
||||||
|
form.accessible_paths,
|
||||||
form.name,
|
form.name,
|
||||||
form.description,
|
form.description,
|
||||||
form.instructions,
|
form.instructions,
|
||||||
form.accessible_paths,
|
|
||||||
form.allowed_tools,
|
form.allowed_tools,
|
||||||
form.configuration,
|
form.configuration,
|
||||||
agent,
|
agent,
|
||||||
onClose,
|
onClose,
|
||||||
t,
|
t,
|
||||||
updateAgent,
|
updateAgent,
|
||||||
|
afterSubmit,
|
||||||
addAgent
|
addAgent
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
export type UpdateAgentBaseOptions = {
|
|
||||||
/** Whether to show success toast after updating. Defaults to true. */
|
|
||||||
showSuccessToast?: boolean
|
|
||||||
}
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { ListAgentsResponse, UpdateAgentForm } from '@renderer/types'
|
import { AgentEntity, ListAgentsResponse, UpdateAgentForm } from '@renderer/types'
|
||||||
|
import { UpdateAgentBaseOptions, UpdateAgentFunction } from '@renderer/types/agent'
|
||||||
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
|
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { mutate } from 'swr'
|
import { mutate } from 'swr'
|
||||||
|
|
||||||
import { UpdateAgentBaseOptions } from './types'
|
|
||||||
import { useAgentClient } from './useAgentClient'
|
import { useAgentClient } from './useAgentClient'
|
||||||
|
|
||||||
export const useUpdateAgent = () => {
|
export const useUpdateAgent = () => {
|
||||||
@ -12,8 +12,8 @@ export const useUpdateAgent = () => {
|
|||||||
const client = useAgentClient()
|
const client = useAgentClient()
|
||||||
const listKey = client.agentPaths.base
|
const listKey = client.agentPaths.base
|
||||||
|
|
||||||
const updateAgent = useCallback(
|
const updateAgent: UpdateAgentFunction = useCallback(
|
||||||
async (form: UpdateAgentForm, options?: UpdateAgentBaseOptions) => {
|
async (form: UpdateAgentForm, options?: UpdateAgentBaseOptions): Promise<AgentEntity | undefined> => {
|
||||||
try {
|
try {
|
||||||
const itemKey = client.agentPaths.withId(form.id)
|
const itemKey = client.agentPaths.withId(form.id)
|
||||||
// may change to optimistic update
|
// may change to optimistic update
|
||||||
@ -23,8 +23,10 @@ export const useUpdateAgent = () => {
|
|||||||
if (options?.showSuccessToast ?? true) {
|
if (options?.showSuccessToast ?? true) {
|
||||||
window.toast.success(t('common.update_success'))
|
window.toast.success(t('common.update_success'))
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.update.error.failed')))
|
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.update.error.failed')))
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[client, listKey, t]
|
[client, listKey, t]
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
import { ListAgentSessionsResponse, UpdateSessionForm } from '@renderer/types'
|
import { AgentSessionEntity, ListAgentSessionsResponse, UpdateSessionForm } from '@renderer/types'
|
||||||
|
import { UpdateAgentBaseOptions, UpdateAgentSessionFunction } from '@renderer/types/agent'
|
||||||
import { getErrorMessage } from '@renderer/utils/error'
|
import { getErrorMessage } from '@renderer/utils/error'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { mutate } from 'swr'
|
import { mutate } from 'swr'
|
||||||
|
|
||||||
import { UpdateAgentBaseOptions } from './types'
|
|
||||||
import { useAgentClient } from './useAgentClient'
|
import { useAgentClient } from './useAgentClient'
|
||||||
|
|
||||||
export const useUpdateSession = (agentId: string | null) => {
|
export const useUpdateSession = (agentId: string | null) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const client = useAgentClient()
|
const client = useAgentClient()
|
||||||
|
|
||||||
const updateSession = useCallback(
|
const updateSession: UpdateAgentSessionFunction = useCallback(
|
||||||
async (form: UpdateSessionForm, options?: UpdateAgentBaseOptions) => {
|
async (form: UpdateSessionForm, options?: UpdateAgentBaseOptions): Promise<AgentSessionEntity | undefined> => {
|
||||||
if (!agentId) return
|
if (!agentId) return
|
||||||
const paths = client.getSessionPaths(agentId)
|
const paths = client.getSessionPaths(agentId)
|
||||||
const listKey = paths.base
|
const listKey = paths.base
|
||||||
@ -29,8 +29,10 @@ export const useUpdateSession = (agentId: string | null) => {
|
|||||||
if (options?.showSuccessToast ?? true) {
|
if (options?.showSuccessToast ?? true) {
|
||||||
window.toast.success(t('common.update_success'))
|
window.toast.success(t('common.update_success'))
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.toast.error({ title: t('agent.session.update.error.failed'), description: getErrorMessage(error) })
|
window.toast.error({ title: t('agent.session.update.error.failed'), description: getErrorMessage(error) })
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[agentId, client, t]
|
[agentId, client, t]
|
||||||
|
|||||||
@ -10,13 +10,13 @@ import { loadTopicMessagesThunk } from '@renderer/store/thunk/messageThunk'
|
|||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { findMainTextBlocks } from '@renderer/utils/messageUtils/find'
|
import { findMainTextBlocks } from '@renderer/utils/messageUtils/find'
|
||||||
import { find, isEmpty } from 'lodash'
|
import { find, isEmpty } from 'lodash'
|
||||||
import { useEffect, useState } from 'react'
|
import { type Dispatch, type SetStateAction, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { useAssistant } from './useAssistant'
|
import { useAssistant } from './useAssistant'
|
||||||
import { getStoreSetting } from './useSettings'
|
import { getStoreSetting } from './useSettings'
|
||||||
|
|
||||||
let _activeTopic: Topic
|
let _activeTopic: Topic
|
||||||
let _setActiveTopic: (topic: Topic) => void
|
let _setActiveTopic: Dispatch<SetStateAction<Topic>>
|
||||||
|
|
||||||
// const logger = loggerService.withContext('useTopic')
|
// const logger = loggerService.withContext('useTopic')
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { useAssistants } from '@renderer/hooks/useAssistant'
|
|||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
|
||||||
import NavigationService from '@renderer/services/NavigationService'
|
import NavigationService from '@renderer/services/NavigationService'
|
||||||
import { newMessagesActions } from '@renderer/store/newMessage'
|
import { newMessagesActions } from '@renderer/store/newMessage'
|
||||||
import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store/runtime'
|
import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store/runtime'
|
||||||
@ -33,8 +32,10 @@ const HomePage: FC = () => {
|
|||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const state = location.state
|
const state = location.state
|
||||||
|
|
||||||
const [activeAssistant, _setActiveAssistant] = useState(state?.assistant || _activeAssistant || assistants[0])
|
const [activeAssistant, _setActiveAssistant] = useState<Assistant>(
|
||||||
const { activeTopic, setActiveTopic: _setActiveTopic } = useActiveTopic(activeAssistant?.id, state?.topic)
|
state?.assistant || _activeAssistant || assistants[0]
|
||||||
|
)
|
||||||
|
const { activeTopic, setActiveTopic: _setActiveTopic } = useActiveTopic(activeAssistant?.id ?? '', state?.topic)
|
||||||
const { showAssistants, showTopics, topicPosition } = useSettings()
|
const { showAssistants, showTopics, topicPosition } = useSettings()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const { chat } = useRuntime()
|
const { chat } = useRuntime()
|
||||||
@ -43,16 +44,20 @@ const HomePage: FC = () => {
|
|||||||
_activeAssistant = activeAssistant
|
_activeAssistant = activeAssistant
|
||||||
|
|
||||||
const setActiveAssistant = useCallback(
|
const setActiveAssistant = useCallback(
|
||||||
|
// TODO: allow to set it as null.
|
||||||
(newAssistant: Assistant) => {
|
(newAssistant: Assistant) => {
|
||||||
if (newAssistant.id === activeAssistant.id) return
|
if (newAssistant.id === activeAssistant?.id) return
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
_setActiveAssistant(newAssistant)
|
_setActiveAssistant(newAssistant)
|
||||||
|
if (newAssistant.id !== 'fake') {
|
||||||
|
dispatch(setActiveAgentId(null))
|
||||||
|
}
|
||||||
// 同步更新 active topic,避免不必要的重新渲染
|
// 同步更新 active topic,避免不必要的重新渲染
|
||||||
const newTopic = newAssistant.topics[0]
|
const newTopic = newAssistant.topics[0]
|
||||||
_setActiveTopic((prev) => (newTopic?.id === prev.id ? prev : newTopic))
|
_setActiveTopic((prev) => (newTopic?.id === prev.id ? prev : newTopic))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[_setActiveTopic, activeAssistant]
|
[_setActiveTopic, activeAssistant?.id, dispatch]
|
||||||
)
|
)
|
||||||
|
|
||||||
const setActiveTopic = useCallback(
|
const setActiveTopic = useCallback(
|
||||||
@ -76,19 +81,6 @@ const HomePage: FC = () => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const unsubscribe = EventEmitter.on(EVENT_NAMES.SWITCH_ASSISTANT, (assistantId: string) => {
|
|
||||||
const newAssistant = assistants.find((a) => a.id === assistantId)
|
|
||||||
if (newAssistant) {
|
|
||||||
setActiveAssistant(newAssistant)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unsubscribe()
|
|
||||||
}
|
|
||||||
}, [assistants, setActiveAssistant])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canMinimize = topicPosition == 'left' ? !showAssistants : !showAssistants && !showTopics
|
const canMinimize = topicPosition == 'left' ? !showAssistants : !showAssistants && !showTopics
|
||||||
window.api.window.setMinimumSize(canMinimize ? SECOND_MIN_WINDOW_WIDTH : MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
|
window.api.window.setMinimumSize(canMinimize ? SECOND_MIN_WINDOW_WIDTH : MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
|
||||||
@ -98,29 +90,6 @@ const HomePage: FC = () => {
|
|||||||
}
|
}
|
||||||
}, [showAssistants, showTopics, topicPosition])
|
}, [showAssistants, showTopics, topicPosition])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeTopicOrSession === 'session') {
|
|
||||||
setActiveAssistant({
|
|
||||||
id: 'fake',
|
|
||||||
name: '',
|
|
||||||
prompt: '',
|
|
||||||
topics: [
|
|
||||||
{
|
|
||||||
id: 'fake',
|
|
||||||
assistantId: 'fake',
|
|
||||||
name: 'fake',
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: '',
|
|
||||||
messages: []
|
|
||||||
} as unknown as Topic
|
|
||||||
],
|
|
||||||
type: 'chat'
|
|
||||||
})
|
|
||||||
} else if (activeTopicOrSession === 'topic') {
|
|
||||||
dispatch(setActiveAgentId(null))
|
|
||||||
}
|
|
||||||
}, [activeTopicOrSession, dispatch, setActiveAssistant])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container id="home-page">
|
<Container id="home-page">
|
||||||
{isLeftNavbar && (
|
{isLeftNavbar && (
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { useAssistantsTabSortType } from '@renderer/hooks/useStore'
|
|||||||
import { useTags } from '@renderer/hooks/useTags'
|
import { useTags } from '@renderer/hooks/useTags'
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { addIknowAction } from '@renderer/store/runtime'
|
import { addIknowAction } from '@renderer/store/runtime'
|
||||||
import { Assistant, AssistantsSortType } from '@renderer/types'
|
import { Assistant, AssistantsSortType, Topic } from '@renderer/types'
|
||||||
import { getErrorMessage } from '@renderer/utils'
|
import { getErrorMessage } from '@renderer/utils'
|
||||||
import { FC, useCallback, useRef, useState } from 'react'
|
import { FC, useCallback, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -99,6 +99,30 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
|
|||||||
[setAssistantsTabSortType]
|
[setAssistantsTabSortType]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleAgentPress = useCallback(
|
||||||
|
(agentId: string) => {
|
||||||
|
setActiveAgentId(agentId)
|
||||||
|
// TODO: should allow it to be null
|
||||||
|
setActiveAssistant({
|
||||||
|
id: 'fake',
|
||||||
|
name: '',
|
||||||
|
prompt: '',
|
||||||
|
topics: [
|
||||||
|
{
|
||||||
|
id: 'fake',
|
||||||
|
assistantId: 'fake',
|
||||||
|
name: 'fake',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
messages: []
|
||||||
|
} as unknown as Topic
|
||||||
|
],
|
||||||
|
type: 'chat'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[setActiveAgentId, setActiveAssistant]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="assistants-tab" ref={containerRef}>
|
<Container className="assistants-tab" ref={containerRef}>
|
||||||
{!apiServerConfig.enabled && !apiServerRunning && !iknow[ALERT_KEY] && (
|
{!apiServerConfig.enabled && !apiServerRunning && !iknow[ALERT_KEY] && (
|
||||||
@ -126,7 +150,11 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<UnifiedAddButton onCreateAssistant={onCreateAssistant} />
|
<UnifiedAddButton
|
||||||
|
onCreateAssistant={onCreateAssistant}
|
||||||
|
setActiveAssistant={setActiveAssistant}
|
||||||
|
setActiveAgentId={setActiveAgentId}
|
||||||
|
/>
|
||||||
|
|
||||||
{assistantsTabSortType === 'tags' ? (
|
{assistantsTabSortType === 'tags' ? (
|
||||||
<UnifiedTagGroups
|
<UnifiedTagGroups
|
||||||
@ -162,7 +190,7 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
|
|||||||
onAssistantSwitch={setActiveAssistant}
|
onAssistantSwitch={setActiveAssistant}
|
||||||
onAssistantDelete={onDeleteAssistant}
|
onAssistantDelete={onDeleteAssistant}
|
||||||
onAgentDelete={deleteAgent}
|
onAgentDelete={deleteAgent}
|
||||||
onAgentPress={setActiveAgentId}
|
onAgentPress={handleAgentPress}
|
||||||
addPreset={addAssistantPreset}
|
addPreset={addAssistantPreset}
|
||||||
copyAssistant={copyAssistant}
|
copyAssistant={copyAssistant}
|
||||||
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
||||||
|
|||||||
@ -1,19 +1,25 @@
|
|||||||
import { Button, Popover, PopoverContent, PopoverTrigger, useDisclosure } from '@heroui/react'
|
import { Button, Popover, PopoverContent, PopoverTrigger, useDisclosure } from '@heroui/react'
|
||||||
import { AgentModal } from '@renderer/components/Popups/agent/AgentModal'
|
import { AgentModal } from '@renderer/components/Popups/agent/AgentModal'
|
||||||
|
import { useAppDispatch } from '@renderer/store'
|
||||||
|
import { setActiveTopicOrSessionAction } from '@renderer/store/runtime'
|
||||||
|
import { AgentEntity, Assistant, Topic } from '@renderer/types'
|
||||||
import { Bot, MessageSquare } from 'lucide-react'
|
import { Bot, MessageSquare } from 'lucide-react'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import AddButton from './AddButton'
|
import AddButton from './AddButton'
|
||||||
|
|
||||||
interface UnifiedAddButtonProps {
|
interface UnifiedAddButtonProps {
|
||||||
onCreateAssistant: () => void
|
onCreateAssistant: () => void
|
||||||
|
setActiveAssistant: (a: Assistant) => void
|
||||||
|
setActiveAgentId: (id: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant }) => {
|
const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant, setActiveAssistant, setActiveAgentId }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
||||||
const { isOpen: isAgentModalOpen, onOpen: onAgentModalOpen, onClose: onAgentModalClose } = useDisclosure()
|
const { isOpen: isAgentModalOpen, onOpen: onAgentModalOpen, onClose: onAgentModalClose } = useDisclosure()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const handleAddAssistant = () => {
|
const handleAddAssistant = () => {
|
||||||
setIsPopoverOpen(false)
|
setIsPopoverOpen(false)
|
||||||
@ -25,6 +31,31 @@ const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant }) => {
|
|||||||
onAgentModalOpen()
|
onAgentModalOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const afterCreate = useCallback(
|
||||||
|
(a: AgentEntity) => {
|
||||||
|
// TODO: should allow it to be null
|
||||||
|
setActiveAssistant({
|
||||||
|
id: 'fake',
|
||||||
|
name: '',
|
||||||
|
prompt: '',
|
||||||
|
topics: [
|
||||||
|
{
|
||||||
|
id: 'fake',
|
||||||
|
assistantId: 'fake',
|
||||||
|
name: 'fake',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
messages: []
|
||||||
|
} as unknown as Topic
|
||||||
|
],
|
||||||
|
type: 'chat'
|
||||||
|
})
|
||||||
|
setActiveAgentId(a.id)
|
||||||
|
dispatch(setActiveTopicOrSessionAction('session'))
|
||||||
|
},
|
||||||
|
[dispatch, setActiveAgentId, setActiveAssistant]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-1">
|
<div className="mb-1">
|
||||||
<Popover
|
<Popover
|
||||||
@ -53,7 +84,7 @@ const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant }) => {
|
|||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<AgentModal isOpen={isAgentModalOpen} onClose={onAgentModalClose} />
|
<AgentModal isOpen={isAgentModalOpen} onClose={onAgentModalClose} afterSubmit={afterCreate} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import { useRuntime } from '@renderer/hooks/useRuntime'
|
|||||||
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShowTopics } from '@renderer/hooks/useStore'
|
import { useShowTopics } from '@renderer/hooks/useStore'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
|
import { useAppDispatch } from '@renderer/store'
|
||||||
|
import { setActiveAgentId, setActiveTopicOrSessionAction } from '@renderer/store/runtime'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { Tab } from '@renderer/types/chat'
|
import { Tab } from '@renderer/types/chat'
|
||||||
import { classNames, getErrorMessage, uuid } from '@renderer/utils'
|
import { classNames, getErrorMessage, uuid } from '@renderer/utils'
|
||||||
@ -50,6 +52,7 @@ const HomeTabs: FC<Props> = ({
|
|||||||
const { activeTopicOrSession, activeAgentId } = chat
|
const { activeTopicOrSession, activeAgentId } = chat
|
||||||
const { session, isLoading: isSessionLoading, error: sessionError } = useActiveSession()
|
const { session, isLoading: isSessionLoading, error: sessionError } = useActiveSession()
|
||||||
const { updateSession } = useUpdateSession(activeAgentId)
|
const { updateSession } = useUpdateSession(activeAgentId)
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const isSessionView = activeTopicOrSession === 'session'
|
const isSessionView = activeTopicOrSession === 'session'
|
||||||
const isTopicView = activeTopicOrSession === 'topic'
|
const isTopicView = activeTopicOrSession === 'topic'
|
||||||
@ -69,13 +72,19 @@ const HomeTabs: FC<Props> = ({
|
|||||||
|
|
||||||
const onCreateAssistant = async () => {
|
const onCreateAssistant = async () => {
|
||||||
const assistant = await AddAssistantPopup.show()
|
const assistant = await AddAssistantPopup.show()
|
||||||
assistant && setActiveAssistant(assistant)
|
if (assistant) {
|
||||||
|
setActiveAssistant(assistant)
|
||||||
|
dispatch(setActiveAgentId(null))
|
||||||
|
dispatch(setActiveTopicOrSessionAction('topic'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCreateDefaultAssistant = () => {
|
const onCreateDefaultAssistant = () => {
|
||||||
const assistant = { ...defaultAssistant, id: uuid() }
|
const assistant = { ...defaultAssistant, id: uuid() }
|
||||||
addAssistant(assistant)
|
addAssistant(assistant)
|
||||||
setActiveAssistant(assistant)
|
setActiveAssistant(assistant)
|
||||||
|
dispatch(setActiveAgentId(null))
|
||||||
|
dispatch(setActiveTopicOrSessionAction('topic'))
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -62,7 +62,12 @@ const ChatNavbarContent: FC<Props> = ({ assistant }) => {
|
|||||||
)}
|
)}
|
||||||
{activeSession && (
|
{activeSession && (
|
||||||
<BreadcrumbItem>
|
<BreadcrumbItem>
|
||||||
<SelectAgentBaseModelButton agentBase={activeSession} onSelect={handleUpdateModel} />
|
<SelectAgentBaseModelButton
|
||||||
|
agentBase={activeSession}
|
||||||
|
onSelect={async (model) => {
|
||||||
|
await handleUpdateModel(model)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
)}
|
)}
|
||||||
{activeAgent && activeSession && (
|
{activeAgent && activeSession && (
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import { Button, Tooltip } from '@heroui/react'
|
import { Button, Tooltip } from '@heroui/react'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { AgentBaseWithId, UpdateAgentBaseForm } from '@renderer/types'
|
import { AgentBaseWithId, UpdateAgentBaseForm, UpdateAgentFunctionUnion } from '@renderer/types'
|
||||||
import { Plus } from 'lucide-react'
|
import { Plus } from 'lucide-react'
|
||||||
import React, { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { SettingsItem, SettingsTitle } from './shared'
|
import { SettingsItem, SettingsTitle } from './shared'
|
||||||
|
|
||||||
export interface AccessibleDirsSettingProps {
|
export interface AccessibleDirsSettingProps {
|
||||||
base: AgentBaseWithId | undefined | null
|
base: AgentBaseWithId | undefined | null
|
||||||
update: (form: UpdateAgentBaseForm) => Promise<void>
|
update: UpdateAgentFunctionUnion
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = loggerService.withContext('AccessibleDirsSetting')
|
const logger = loggerService.withContext('AccessibleDirsSetting')
|
||||||
|
|
||||||
export const AccessibleDirsSetting: React.FC<AccessibleDirsSettingProps> = ({ base, update }) => {
|
export const AccessibleDirsSetting = ({ base, update }: AccessibleDirsSettingProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const updateAccessiblePaths = useCallback(
|
const updateAccessiblePaths = useCallback(
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
import { EmojiAvatarWithPicker } from '@renderer/components/Avatar/EmojiAvatarWithPicker'
|
import { EmojiAvatarWithPicker } from '@renderer/components/Avatar/EmojiAvatarWithPicker'
|
||||||
import { AgentConfigurationSchema, AgentEntity, isAgentType, UpdateAgentForm } from '@renderer/types'
|
import {
|
||||||
|
AgentConfigurationSchema,
|
||||||
|
AgentEntity,
|
||||||
|
isAgentType,
|
||||||
|
UpdateAgentForm,
|
||||||
|
UpdateAgentFunction
|
||||||
|
} from '@renderer/types'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
@ -7,7 +13,7 @@ import { SettingsItem, SettingsTitle } from './shared'
|
|||||||
|
|
||||||
export interface AvatarSettingsProps {
|
export interface AvatarSettingsProps {
|
||||||
agent: AgentEntity
|
agent: AgentEntity
|
||||||
update: (form: UpdateAgentForm) => Promise<void>
|
update: UpdateAgentFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
// const logger = loggerService.withContext('AvatarSetting')
|
// const logger = loggerService.withContext('AvatarSetting')
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { Textarea } from '@heroui/react'
|
import { Textarea } from '@heroui/react'
|
||||||
import { AgentBaseWithId, UpdateAgentBaseForm } from '@renderer/types'
|
import { AgentBaseWithId, UpdateAgentBaseForm, UpdateAgentFunctionUnion } from '@renderer/types'
|
||||||
import React, { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { SettingsItem, SettingsTitle } from './shared'
|
import { SettingsItem, SettingsTitle } from './shared'
|
||||||
|
|
||||||
export interface DescriptionSettingProps {
|
export interface DescriptionSettingProps {
|
||||||
base: AgentBaseWithId | undefined | null
|
base: AgentBaseWithId | undefined | null
|
||||||
update: (form: UpdateAgentBaseForm) => Promise<void>
|
update: UpdateAgentFunctionUnion
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DescriptionSetting: React.FC<DescriptionSettingProps> = ({ base, update }) => {
|
export const DescriptionSetting = ({ base, update }: DescriptionSettingProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [description, setDescription] = useState<string | undefined>(base?.description?.trim())
|
const [description, setDescription] = useState<string | undefined>(base?.description?.trim())
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,9 @@ const EssentialSettings: FC<EssentialSettingsProps> = ({ agentBase, update, show
|
|||||||
</div>
|
</div>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
)}
|
)}
|
||||||
{isAgent && <AvatarSetting agent={agentBase} update={update} />}
|
{isAgent && (
|
||||||
|
<AvatarSetting agent={agentBase} update={update as ReturnType<typeof useUpdateAgent>['updateAgent']} />
|
||||||
|
)}
|
||||||
<NameSetting base={agentBase} update={update} />
|
<NameSetting base={agentBase} update={update} />
|
||||||
{showModelSetting && <ModelSetting base={agentBase} update={update} />}
|
{showModelSetting && <ModelSetting base={agentBase} update={update} />}
|
||||||
<AccessibleDirsSetting base={agentBase} update={update} />
|
<AccessibleDirsSetting base={agentBase} update={update} />
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import SelectAgentBaseModelButton from '@renderer/pages/home/components/SelectAgentBaseModelButton'
|
import SelectAgentBaseModelButton from '@renderer/pages/home/components/SelectAgentBaseModelButton'
|
||||||
import { AgentBaseWithId, ApiModel, UpdateAgentBaseForm } from '@renderer/types'
|
import { AgentBaseWithId, ApiModel, UpdateAgentFunctionUnion } from '@renderer/types'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { SettingsItem, SettingsTitle } from './shared'
|
import { SettingsItem, SettingsTitle } from './shared'
|
||||||
|
|
||||||
export interface ModelSettingProps {
|
export interface ModelSettingProps {
|
||||||
base: AgentBaseWithId | undefined | null
|
base: AgentBaseWithId | undefined | null
|
||||||
update: (form: UpdateAgentBaseForm) => Promise<void>
|
update: UpdateAgentFunctionUnion
|
||||||
isDisabled?: boolean
|
isDisabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModelSetting: React.FC<ModelSettingProps> = ({ base, update, isDisabled }) => {
|
export const ModelSetting = ({ base, update, isDisabled }: ModelSettingProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const updateModel = async (model: ApiModel) => {
|
const updateModel = async (model: ApiModel) => {
|
||||||
@ -23,7 +23,13 @@ export const ModelSetting: React.FC<ModelSettingProps> = ({ base, update, isDisa
|
|||||||
return (
|
return (
|
||||||
<SettingsItem inline>
|
<SettingsItem inline>
|
||||||
<SettingsTitle id="model">{t('common.model')}</SettingsTitle>
|
<SettingsTitle id="model">{t('common.model')}</SettingsTitle>
|
||||||
<SelectAgentBaseModelButton agentBase={base} onSelect={updateModel} isDisabled={isDisabled} />
|
<SelectAgentBaseModelButton
|
||||||
|
agentBase={base}
|
||||||
|
onSelect={async (model) => {
|
||||||
|
await updateModel(model)
|
||||||
|
}}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Input } from '@heroui/react'
|
import { Input } from '@heroui/react'
|
||||||
import { AgentBaseWithId, UpdateAgentBaseForm } from '@renderer/types'
|
import { AgentBaseWithId, UpdateAgentBaseForm, UpdateAgentFunctionUnion } from '@renderer/types'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
@ -7,10 +7,10 @@ import { SettingsItem, SettingsTitle } from './shared'
|
|||||||
|
|
||||||
export interface NameSettingsProps {
|
export interface NameSettingsProps {
|
||||||
base: AgentBaseWithId | undefined | null
|
base: AgentBaseWithId | undefined | null
|
||||||
update: (form: UpdateAgentBaseForm) => Promise<void>
|
update: UpdateAgentFunctionUnion
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NameSetting: React.FC<NameSettingsProps> = ({ base, update }) => {
|
export const NameSetting = ({ base, update }: NameSettingsProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [name, setName] = useState<string | undefined>(base?.name?.trim())
|
const [name, setName] = useState<string | undefined>(base?.name?.trim())
|
||||||
const updateName = async (name: UpdateAgentBaseForm['name']) => {
|
const updateName = async (name: UpdateAgentBaseForm['name']) => {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Card, CardBody, Tab, Tabs } from '@heroui/react'
|
import { Card, CardBody, Tab, Tabs } from '@heroui/react'
|
||||||
import { useAvailablePlugins, useInstalledPlugins, usePluginActions } from '@renderer/hooks/usePlugins'
|
import { useAvailablePlugins, useInstalledPlugins, usePluginActions } from '@renderer/hooks/usePlugins'
|
||||||
import { GetAgentResponse, GetAgentSessionResponse, UpdateAgentBaseForm } from '@renderer/types/agent'
|
import { GetAgentResponse, GetAgentSessionResponse, UpdateAgentFunctionUnion } from '@renderer/types/agent'
|
||||||
import { FC, useCallback } from 'react'
|
import { FC, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ import { SettingsContainer } from './shared'
|
|||||||
|
|
||||||
interface PluginSettingsProps {
|
interface PluginSettingsProps {
|
||||||
agentBase: GetAgentResponse | GetAgentSessionResponse
|
agentBase: GetAgentResponse | GetAgentSessionResponse
|
||||||
update: (partial: UpdateAgentBaseForm) => Promise<void>
|
update: UpdateAgentFunctionUnion
|
||||||
}
|
}
|
||||||
|
|
||||||
const PluginSettings: FC<PluginSettingsProps> = ({ agentBase }) => {
|
const PluginSettings: FC<PluginSettingsProps> = ({ agentBase }) => {
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import {
|
|||||||
PermissionMode,
|
PermissionMode,
|
||||||
Tool,
|
Tool,
|
||||||
UpdateAgentBaseForm,
|
UpdateAgentBaseForm,
|
||||||
UpdateAgentForm,
|
UpdateAgentFunction,
|
||||||
UpdateSessionForm
|
UpdateAgentSessionFunction
|
||||||
} from '@renderer/types'
|
} from '@renderer/types'
|
||||||
import { Modal } from 'antd'
|
import { Modal } from 'antd'
|
||||||
import { ShieldAlert, ShieldCheck, Wrench } from 'lucide-react'
|
import { ShieldAlert, ShieldCheck, Wrench } from 'lucide-react'
|
||||||
@ -23,11 +23,11 @@ import { SettingsContainer, SettingsItem, SettingsTitle } from './shared'
|
|||||||
type AgentToolingSettingsProps =
|
type AgentToolingSettingsProps =
|
||||||
| {
|
| {
|
||||||
agentBase: GetAgentResponse | undefined | null
|
agentBase: GetAgentResponse | undefined | null
|
||||||
update: (form: UpdateAgentForm) => Promise<void> | void
|
update: UpdateAgentFunction
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
agentBase: GetAgentSessionResponse | undefined | null
|
agentBase: GetAgentSessionResponse | undefined | null
|
||||||
update: (form: UpdateSessionForm) => Promise<void> | void
|
update: UpdateAgentSessionFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentConfigurationState = AgentConfiguration & Record<string, unknown>
|
type AgentConfigurationState = AgentConfiguration & Record<string, unknown>
|
||||||
|
|||||||
@ -18,7 +18,6 @@ export const EVENT_NAMES = {
|
|||||||
SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS',
|
SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS',
|
||||||
SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR',
|
SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR',
|
||||||
SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR',
|
SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR',
|
||||||
SWITCH_ASSISTANT: 'SWITCH_ASSISTANT',
|
|
||||||
NEW_CONTEXT: 'NEW_CONTEXT',
|
NEW_CONTEXT: 'NEW_CONTEXT',
|
||||||
NEW_BRANCH: 'NEW_BRANCH',
|
NEW_BRANCH: 'NEW_BRANCH',
|
||||||
COPY_TOPIC_IMAGE: 'COPY_TOPIC_IMAGE',
|
COPY_TOPIC_IMAGE: 'COPY_TOPIC_IMAGE',
|
||||||
|
|||||||
@ -227,6 +227,25 @@ export type SessionForm = CreateSessionForm | UpdateSessionForm
|
|||||||
|
|
||||||
export type UpdateAgentBaseForm = Partial<AgentBase> & { id: string }
|
export type UpdateAgentBaseForm = Partial<AgentBase> & { id: string }
|
||||||
|
|
||||||
|
// --------------------- Components & Hooks ----------------------
|
||||||
|
|
||||||
|
export type UpdateAgentBaseOptions = {
|
||||||
|
/** Whether to show success toast after updating. Defaults to true. */
|
||||||
|
showSuccessToast?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateAgentFunction = (
|
||||||
|
form: UpdateAgentForm,
|
||||||
|
options?: UpdateAgentBaseOptions
|
||||||
|
) => Promise<AgentEntity | undefined>
|
||||||
|
|
||||||
|
export type UpdateAgentSessionFunction = (
|
||||||
|
form: UpdateSessionForm,
|
||||||
|
options?: UpdateAgentBaseOptions
|
||||||
|
) => Promise<AgentSessionEntity | undefined>
|
||||||
|
|
||||||
|
export type UpdateAgentFunctionUnion = UpdateAgentFunction | UpdateAgentSessionFunction
|
||||||
|
|
||||||
// ------------------ API data transfer objects ------------------
|
// ------------------ API data transfer objects ------------------
|
||||||
export interface CreateAgentRequest extends AgentBase {
|
export interface CreateAgentRequest extends AgentBase {
|
||||||
type: AgentType
|
type: AgentType
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user