mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 22:10:21 +08:00
feat: position add button and new items at the top (#10881)
* feat: add sorting support to list agent api * feat: move add button to top * feat: display newly added assistant or agent on top
This commit is contained in:
parent
f4d7c90126
commit
296f71ed8a
@ -132,6 +132,20 @@ export const createAgent = async (req: Request, res: Response): Promise<Response
|
|||||||
* minimum: 0
|
* minimum: 0
|
||||||
* default: 0
|
* default: 0
|
||||||
* description: Number of agents to skip
|
* description: Number of agents to skip
|
||||||
|
* - in: query
|
||||||
|
* name: sortBy
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* enum: [created_at, updated_at, name]
|
||||||
|
* default: created_at
|
||||||
|
* description: Field to sort by
|
||||||
|
* - in: query
|
||||||
|
* name: orderBy
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* enum: [asc, desc]
|
||||||
|
* default: desc
|
||||||
|
* description: Sort order (asc = ascending, desc = descending)
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: List of agents
|
* description: List of agents
|
||||||
@ -170,10 +184,12 @@ export const listAgents = async (req: Request, res: Response): Promise<Response>
|
|||||||
try {
|
try {
|
||||||
const limit = req.query.limit ? parseInt(req.query.limit as string) : 20
|
const limit = req.query.limit ? parseInt(req.query.limit as string) : 20
|
||||||
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0
|
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0
|
||||||
|
const sortBy = (req.query.sortBy as 'created_at' | 'updated_at' | 'name') || 'created_at'
|
||||||
|
const orderBy = (req.query.orderBy as 'asc' | 'desc') || 'desc'
|
||||||
|
|
||||||
logger.debug('Listing agents', { limit, offset })
|
logger.debug('Listing agents', { limit, offset, sortBy, orderBy })
|
||||||
|
|
||||||
const result = await agentService.listAgents({ limit, offset })
|
const result = await agentService.listAgents({ limit, offset, sortBy, orderBy })
|
||||||
|
|
||||||
logger.info('Agents listed', {
|
logger.info('Agents listed', {
|
||||||
returned: result.agents.length,
|
returned: result.agents.length,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
UpdateAgentRequest,
|
UpdateAgentRequest,
|
||||||
UpdateAgentResponse
|
UpdateAgentResponse
|
||||||
} from '@types'
|
} from '@types'
|
||||||
import { count, eq } from 'drizzle-orm'
|
import { asc, count, desc, eq } from 'drizzle-orm'
|
||||||
|
|
||||||
import { BaseService } from '../BaseService'
|
import { BaseService } from '../BaseService'
|
||||||
import { type AgentRow, agentsTable, type InsertAgentRow } from '../database/schema'
|
import { type AgentRow, agentsTable, type InsertAgentRow } from '../database/schema'
|
||||||
@ -100,7 +100,13 @@ export class AgentService extends BaseService {
|
|||||||
|
|
||||||
const totalResult = await this.database.select({ count: count() }).from(agentsTable)
|
const totalResult = await this.database.select({ count: count() }).from(agentsTable)
|
||||||
|
|
||||||
const baseQuery = this.database.select().from(agentsTable).orderBy(agentsTable.created_at)
|
const sortBy = options.sortBy || 'created_at'
|
||||||
|
const orderBy = options.orderBy || 'desc'
|
||||||
|
|
||||||
|
const sortField = agentsTable[sortBy]
|
||||||
|
const orderFn = orderBy === 'asc' ? asc : desc
|
||||||
|
|
||||||
|
const baseQuery = this.database.select().from(agentsTable).orderBy(orderFn(sortField))
|
||||||
|
|
||||||
const result =
|
const result =
|
||||||
options.limit !== undefined
|
options.limit !== undefined
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import {
|
|||||||
ListAgentSessionsResponseSchema,
|
ListAgentSessionsResponseSchema,
|
||||||
type ListAgentsResponse,
|
type ListAgentsResponse,
|
||||||
ListAgentsResponseSchema,
|
ListAgentsResponseSchema,
|
||||||
|
ListOptions,
|
||||||
objectEntries,
|
objectEntries,
|
||||||
objectKeys,
|
objectKeys,
|
||||||
UpdateAgentForm,
|
UpdateAgentForm,
|
||||||
@ -95,10 +96,19 @@ export class AgentApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async listAgents(): Promise<ListAgentsResponse> {
|
public async listAgents(options?: ListOptions): Promise<ListAgentsResponse> {
|
||||||
const url = this.agentPaths.base
|
const url = this.agentPaths.base
|
||||||
try {
|
try {
|
||||||
const response = await this.axios.get(url)
|
const params = new URLSearchParams()
|
||||||
|
if (options?.limit !== undefined) params.append('limit', String(options.limit))
|
||||||
|
if (options?.offset !== undefined) params.append('offset', String(options.offset))
|
||||||
|
if (options?.sortBy) params.append('sortBy', options.sortBy)
|
||||||
|
if (options?.orderBy) params.append('orderBy', options.orderBy)
|
||||||
|
|
||||||
|
const queryString = params.toString()
|
||||||
|
const fullUrl = queryString ? `${url}?${queryString}` : url
|
||||||
|
|
||||||
|
const response = await this.axios.get(fullUrl)
|
||||||
const result = ListAgentsResponseSchema.safeParse(response.data)
|
const result = ListAgentsResponseSchema.safeParse(response.data)
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error('Not a valid Agents array.')
|
throw new Error('Not a valid Agents array.')
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export const useAgents = () => {
|
|||||||
if (!apiServerRunning) {
|
if (!apiServerRunning) {
|
||||||
throw new Error(t('agent.server.error.not_running'))
|
throw new Error(t('agent.server.error.not_running'))
|
||||||
}
|
}
|
||||||
const result = await client.listAgents()
|
const result = await client.listAgents({ sortBy: 'created_at', orderBy: 'desc' })
|
||||||
// NOTE: We only use the array for now. useUpdateAgent depends on this behavior.
|
// NOTE: We only use the array for now. useUpdateAgent depends on this behavior.
|
||||||
return result.data
|
return result.data
|
||||||
}, [apiServerConfig.enabled, apiServerRunning, client, t])
|
}, [apiServerConfig.enabled, apiServerRunning, client, t])
|
||||||
|
|||||||
@ -126,6 +126,8 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<UnifiedAddButton onCreateAssistant={onCreateAssistant} />
|
||||||
|
|
||||||
{assistantsTabSortType === 'tags' ? (
|
{assistantsTabSortType === 'tags' ? (
|
||||||
<UnifiedTagGroups
|
<UnifiedTagGroups
|
||||||
groupedItems={groupedUnifiedItems}
|
groupedItems={groupedUnifiedItems}
|
||||||
@ -170,8 +172,6 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<UnifiedAddButton onCreateAssistant={onCreateAssistant} />
|
|
||||||
|
|
||||||
{!dragging && <div style={{ minHeight: 10 }}></div>}
|
{!dragging && <div style={{ minHeight: 10 }}></div>}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -43,9 +43,11 @@ export const useUnifiedItems = (options: UseUnifiedItemsOptions) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add new items (not in saved order) to the end
|
// Add new items (not in saved order) to the beginning
|
||||||
availableAgents.forEach((agent) => items.push({ type: 'agent', data: agent }))
|
const newItems: UnifiedItem[] = []
|
||||||
availableAssistants.forEach((assistant) => items.push({ type: 'assistant', data: assistant }))
|
availableAgents.forEach((agent) => newItems.push({ type: 'agent', data: agent }))
|
||||||
|
availableAssistants.forEach((assistant) => newItems.push({ type: 'assistant', data: assistant }))
|
||||||
|
items.unshift(...newItems)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}, [agents, assistants, apiServerEnabled, agentsLoading, agentsError, unifiedListOrder])
|
}, [agents, assistants, apiServerEnabled, agentsLoading, agentsError, unifiedListOrder])
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const assistantsSlice = createSlice({
|
|||||||
state.assistants = action.payload
|
state.assistants = action.payload
|
||||||
},
|
},
|
||||||
addAssistant: (state, action: PayloadAction<Assistant>) => {
|
addAssistant: (state, action: PayloadAction<Assistant>) => {
|
||||||
state.assistants.push(action.payload)
|
state.assistants.unshift(action.payload)
|
||||||
},
|
},
|
||||||
insertAssistant: (state, action: PayloadAction<{ index: number; assistant: Assistant }>) => {
|
insertAssistant: (state, action: PayloadAction<{ index: number; assistant: Assistant }>) => {
|
||||||
const { index, assistant } = action.payload
|
const { index, assistant } = action.payload
|
||||||
|
|||||||
@ -121,6 +121,8 @@ export const isAgentEntity = (value: unknown): value is AgentEntity => {
|
|||||||
export interface ListOptions {
|
export interface ListOptions {
|
||||||
limit?: number
|
limit?: number
|
||||||
offset?: number
|
offset?: number
|
||||||
|
sortBy?: 'created_at' | 'updated_at' | 'name'
|
||||||
|
orderBy?: 'asc' | 'desc'
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentSession entity representing a conversation session with one or more agents
|
// AgentSession entity representing a conversation session with one or more agents
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user