feat(agents): improve error handling and form validation in agent management

- Add getErrorMessage utility for consistent error message formatting
- Enhance addAgent to return Result type for better error handling
- Add disallowEmptySelection to form dropdowns
- Reset loading state on errors in AgentModal
This commit is contained in:
icarus 2025-09-27 17:32:19 +08:00
parent a5ceceeca3
commit 86dde5dc0f
3 changed files with 37 additions and 6 deletions

View File

@ -291,6 +291,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
if (isEditing(agent)) {
if (!agent) {
loadingRef.current = false
throw new Error('Agent is required for editing mode')
}
@ -318,8 +319,11 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
allowed_tools: [...form.allowed_tools],
configuration: form.configuration ? { ...form.configuration } : undefined
} satisfies AddAgentForm
addAgent(newAgent)
logger.debug('Added agent', newAgent)
const result = await addAgent(newAgent)
if (!result.success) {
loadingRef.current = false
throw result.error
}
}
loadingRef.current = false
@ -382,6 +386,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
isDisabled={isEditing(agent)}
selectionMode="single"
selectedKeys={[form.type]}
disallowEmptySelection
onChange={onAgentTypeChange}
items={agentOptions}
label={t('agent.type.label')}
@ -399,6 +404,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
isRequired
selectionMode="single"
selectedKeys={form.model ? [form.model] : []}
disallowEmptySelection
onChange={onModelChange}
items={modelOptions}
label={t('common.model')}

View File

@ -1,6 +1,6 @@
import { useAppDispatch } from '@renderer/store'
import { setActiveAgentId, setActiveSessionIdAction } from '@renderer/store/runtime'
import { AddAgentForm } from '@renderer/types'
import { AddAgentForm, CreateAgentResponse } from '@renderer/types'
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
@ -9,6 +9,16 @@ import useSWR from 'swr'
import { useRuntime } from '../useRuntime'
import { useAgentClient } from './useAgentClient'
type Result<T> =
| {
success: true
data: T
}
| {
success: false
error: Error
}
export const useAgents = () => {
const { t } = useTranslation()
const client = useAgentClient()
@ -24,13 +34,20 @@ export const useAgents = () => {
const dispatch = useAppDispatch()
const addAgent = useCallback(
async (form: AddAgentForm) => {
async (form: AddAgentForm): Promise<Result<CreateAgentResponse>> => {
try {
const result = await client.createAgent(form)
mutate((prev) => [...(prev ?? []), result])
window.toast.success(t('common.add_success'))
return { success: true, data: result }
} catch (error) {
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.add.error.failed')))
const errorMessage = formatErrorMessageWithPrefix(error, t('agent.add.error.failed'))
window.toast.error(errorMessage)
if (error instanceof Error) {
return { success: false, error }
} else {
return { success: false, error: new Error(formatErrorMessageWithPrefix(error, t('agent.add.error.failed'))) }
}
}
},
[client, mutate, t]

View File

@ -68,8 +68,16 @@ export function formatErrorMessage(error: unknown): string {
return `Error Details:\n${formattedJson}`
}
export function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
} else {
return t('error.unknown')
}
}
export function formatErrorMessageWithPrefix(error: unknown, prefix: string): string {
const msg = formatErrorMessage(error)
const msg = getErrorMessage(error)
return `${prefix}: ${msg}`
}