feat: prioritize configured tools in the agent prompt picker (#39850)

This commit is contained in:
Joel 2026-07-31 16:50:54 +08:00 committed by GitHub
parent f9966c2ee4
commit 37bf80dcf6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 109 additions and 11 deletions

View File

@ -93,6 +93,33 @@ const mockBuiltInTools = vi.hoisted(() => [
},
])
const wikipediaProvider = {
id: 'wikipedia',
name: 'Wikipedia',
author: 'Dify',
description: { en_US: 'Wikipedia tools' },
icon: 'wikipedia.svg',
icon_dark: 'wikipedia-dark.svg',
label: { en_US: 'Wikipedia' },
type: 'builtin',
team_credentials: {},
is_team_authorization: true,
allow_delete: false,
labels: [],
meta: {},
tools: [
{
name: 'wikipedia_search',
author: 'Dify',
label: { en_US: 'Wikipedia Search' },
description: { en_US: 'Search Wikipedia.' },
parameters: [],
labels: [],
output_schema: {},
},
],
}
vi.mock('@/app/components/base/prompt-editor', () => ({
__esModule: true,
default: (props: PromptEditorProps) => {
@ -929,6 +956,60 @@ describe('AgentPromptEditor', () => {
])
})
it('should show configured providers and actions before other available tools', () => {
mockBuiltInTools.unshift(wikipediaProvider)
const configuredDuckDuckGoTranslateTool: AgentTool = {
...duckDuckGoProviderTool,
actions: [
{
id: 'duckduckgo-translate',
name: 'DuckDuckGo Translate',
toolName: 'ddg_translate',
description: 'Translate search results.',
},
],
}
const view = render(
<AgentPromptSlashMenu
view="tools"
categories={[{ key: 'tools', label: 'Tools', icon: 'i-ri-box-3-line' }]}
skills={[]}
files={[]}
configuredTools={[configuredDuckDuckGoTranslateTool]}
onAddProviderTools={vi.fn()}
knowledgeRetrievals={[]}
onBack={vi.fn()}
onOpenCategory={vi.fn()}
onInsertToken={vi.fn()}
/>,
)
try {
const providerButtons = screen
.getAllByRole('button')
.filter(
(button) =>
button.textContent?.includes('DuckDuckGo') ||
button.textContent?.includes('Wikipedia'),
)
expect(providerButtons).toHaveLength(2)
expect(providerButtons[0]).toHaveTextContent('DuckDuckGo')
expect(providerButtons[1]).toHaveTextContent('Wikipedia')
fireEvent.click(screen.getByRole('button', { name: 'DuckDuckGo' }))
const actionButtons = screen.getAllByRole('button', {
name: /DuckDuckGo (Search|Translate)/,
})
expect(actionButtons[0]).toHaveTextContent('DuckDuckGo Translate')
expect(actionButtons[1]).toHaveTextContent('DuckDuckGo Search')
} finally {
view.unmount()
mockBuiltInTools.shift()
}
})
it('should close the slash menu when the trailing slash is deleted', async () => {
const { setPromptValue } = renderAgentPromptEditor('Review /')

View File

@ -329,20 +329,23 @@ function AgentPromptToolRows({
const configuredCliTools = ENABLE_AGENT_CLI_TOOLS
? configuredTools.filter((tool) => tool.kind === 'cli')
: []
const availableProviders = useMemo(() => {
if (activeTab === 'all') return [...builtInTools, ...workflowTools, ...customTools, ...mcpTools]
if (activeTab === ToolType.BuiltIn) return builtInTools
if (activeTab === ToolType.Workflow) return workflowTools
if (activeTab === ToolType.Custom) return customTools
if (activeTab === ToolType.MCP) return mcpTools
return []
}, [activeTab, builtInTools, customTools, mcpTools, workflowTools])
const selectedTools = useMemo(
() => configuredTools.flatMap(toSelectedToolValue),
[configuredTools],
)
const availableProviders = useMemo(() => {
let providers: ToolWithProvider[] = []
if (activeTab === 'all')
providers = [...builtInTools, ...workflowTools, ...customTools, ...mcpTools]
if (activeTab === ToolType.BuiltIn) providers = builtInTools
if (activeTab === ToolType.Workflow) providers = workflowTools
if (activeTab === ToolType.Custom) providers = customTools
if (activeTab === ToolType.MCP) providers = mcpTools
return prioritizeItems(providers, (provider) =>
provider.tools.some((tool) => isToolSelected(selectedTools, provider, tool)),
)
}, [activeTab, builtInTools, customTools, mcpTools, selectedTools, workflowTools])
const tabs = [
{ key: 'all' as const, label: t(($) => $['agentDetail.configure.tools.toolTabs.all']) },
{
@ -435,7 +438,9 @@ function AgentPromptToolRows({
onToggle={() => toggleProvider(provider.id)}
/>
{expandedProviderIds.has(provider.id) &&
provider.tools.map((tool) => (
prioritizeItems(provider.tools, (tool) =>
isToolSelected(selectedTools, provider, tool),
).map((tool) => (
<AgentPromptProviderToolActionRow
key={tool.name}
tool={tool}
@ -452,6 +457,18 @@ function AgentPromptToolRows({
type ToolPromptTab = ToolType | 'cli'
function prioritizeItems<T>(items: T[], isPriority: (item: T) => boolean) {
const priorityItems: T[] = []
const remainingItems: T[] = []
items.forEach((item) => {
if (isPriority(item)) priorityItems.push(item)
else remainingItems.push(item)
})
return [...priorityItems, ...remainingItems]
}
function getLocalizedText(text: Record<string, string> | undefined | null, language: string) {
if (!text) return ''