refactor(agent): split AgentForm into BaseAgentForm and specific types

Improve type safety by separating AgentForm into BaseAgentForm for shared fields and specific types for add/update operations. This better reflects the actual usage patterns in the API client and modal components.
This commit is contained in:
icarus 2025-09-18 18:21:52 +08:00
parent 21ce139df0
commit 259f2157f6
3 changed files with 20 additions and 15 deletions

View File

@ -1,6 +1,6 @@
import { formatAgentServerError } from '@renderer/utils' import { formatAgentServerError } from '@renderer/utils'
import { import {
AgentForm, AddAgentForm,
AgentServerErrorSchema, AgentServerErrorSchema,
CreateAgentRequest, CreateAgentRequest,
CreateAgentResponse, CreateAgentResponse,
@ -9,6 +9,7 @@ import {
GetAgentResponseSchema, GetAgentResponseSchema,
type ListAgentsResponse, type ListAgentsResponse,
ListAgentsResponseSchema, ListAgentsResponseSchema,
UpdateAgentForm,
UpdateAgentRequest, UpdateAgentRequest,
UpdateAgentResponse, UpdateAgentResponse,
UpdateAgentResponseSchema UpdateAgentResponseSchema
@ -71,12 +72,10 @@ export class AgentApiClient {
} }
} }
public async createAgent(agent: AgentForm): Promise<CreateAgentResponse> { public async createAgent(agent: AddAgentForm): Promise<CreateAgentResponse> {
const url = this.agentPaths.base const url = this.agentPaths.base
try { try {
const payload = { const payload = agent satisfies CreateAgentRequest
...agent
} satisfies CreateAgentRequest
const response = await this.axios.post(url, payload) const response = await this.axios.post(url, payload)
const data = CreateAgentResponseSchema.parse(response.data) const data = CreateAgentResponseSchema.parse(response.data)
return data return data
@ -105,12 +104,10 @@ export class AgentApiClient {
} }
} }
public async updateAgent(id: string, agent: Partial<AgentForm>): Promise<UpdateAgentResponse> { public async updateAgent(id: string, agent: UpdateAgentForm): Promise<UpdateAgentResponse> {
const url = this.agentPaths.withId(id) const url = this.agentPaths.withId(id)
try { try {
const payload = { const payload = agent satisfies UpdateAgentRequest
...agent
} satisfies UpdateAgentRequest
const response = await this.axios.patch(url, payload) const response = await this.axios.patch(url, payload)
const data = UpdateAgentResponseSchema.parse(response.data) const data = UpdateAgentResponseSchema.parse(response.data)
return data return data

View File

@ -21,7 +21,7 @@ import ClaudeIcon from '@renderer/assets/images/models/claude.png'
import { useAgents } from '@renderer/hooks/agents/useAgents' import { useAgents } from '@renderer/hooks/agents/useAgents'
import { useTimer } from '@renderer/hooks/useTimer' import { useTimer } from '@renderer/hooks/useTimer'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { AgentEntity, AgentForm, isAgentType } from '@renderer/types' import { AgentEntity, AgentForm, BaseAgentForm, isAgentType } from '@renderer/types'
import { uuid } from '@renderer/utils' import { uuid } from '@renderer/utils'
import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -44,7 +44,7 @@ interface AgentTypeOption extends Option {
type ModelOption = Option type ModelOption = Option
const buildAgentForm = (existing?: AgentEntity): AgentForm => ({ const buildAgentForm = (existing?: AgentEntity): BaseAgentForm => ({
type: existing?.type ?? 'claude-code', type: existing?.type ?? 'claude-code',
name: existing?.name ?? 'Claude Code', name: existing?.name ?? 'Claude Code',
description: existing?.description, description: existing?.description,
@ -162,7 +162,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
} }
setForm((prev) => ({ setForm((prev) => ({
...prev, ...prev,
type: e.target.value as AgentForm['type'], type: e.target.value as AgentType,
name: newName name: newName
})) }))
}, },
@ -229,7 +229,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
return return
} }
let resultAgent: AgentEntity let resultAgent: BaseAgentForm
if (isEditing(agent)) { if (isEditing(agent)) {
if (!agent) { if (!agent) {
throw new Error('Agent is required for editing mode') throw new Error('Agent is required for editing mode')

View File

@ -118,8 +118,8 @@ export interface SessionMessageContent {
// - mcps: Optional array of MCP (Model Control Protocol) tool IDs // - mcps: Optional array of MCP (Model Control Protocol) tool IDs
// - allowed_tools: Optional array of permitted tool IDs // - allowed_tools: Optional array of permitted tool IDs
// - configuration: Optional agent settings (temperature, top_p, etc.) // - configuration: Optional agent settings (temperature, top_p, etc.)
export type AgentForm = { export interface BaseAgentForm {
type: AgentType // These fileds should be editable by user.
name: string name: string
description?: string description?: string
instructions?: string instructions?: string
@ -127,6 +127,14 @@ export type AgentForm = {
accessible_paths: string[] accessible_paths: string[]
} }
export interface AddAgentForm extends BaseAgentForm {
type: AgentType
}
export type UpdateAgentForm = Partial<BaseAgentForm>
export type AgentForm = AddAgentForm | UpdateAgentForm
// ------------------------ // ------------------------
// API Data Transfer Object // API Data Transfer Object
// ------------------------ // ------------------------