mirror of
https://github.com/langgenius/dify.git
synced 2026-05-09 12:59:18 +08:00
Merge 48341a922e into 19bf36a716
This commit is contained in:
commit
76fa519091
@ -465,4 +465,90 @@ describe('AgentTools', () => {
|
||||
expect((getModelConfig().agentConfig.tools[0] as { isDeleted: boolean }).isDeleted).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
it('should show out-of-sync warning when attached operations are a subset of available', () => {
|
||||
// Provider has 2 tools available (search + translate)
|
||||
builtInTools = [
|
||||
createCollection({
|
||||
id: 'multi-op-provider',
|
||||
name: 'vendor/multi-op-provider',
|
||||
tools: [
|
||||
createToolDefinition({ name: 'op_a' }),
|
||||
createToolDefinition({ name: 'op_b' }),
|
||||
],
|
||||
}),
|
||||
]
|
||||
|
||||
// But the app only attaches op_a
|
||||
renderAgentTools([
|
||||
createAgentTool({
|
||||
provider_id: 'multi-op-provider',
|
||||
provider_name: 'vendor/multi-op-provider',
|
||||
tool_name: 'op_a',
|
||||
tool_label: 'Operation A',
|
||||
}),
|
||||
])
|
||||
|
||||
const warningIcon = screen.getByTestId('tool-ops-out-of-sync')
|
||||
expect(warningIcon).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not show out-of-sync warning when all operations are attached', () => {
|
||||
// Provider has 2 tools available
|
||||
builtInTools = [
|
||||
createCollection({
|
||||
id: 'full-provider',
|
||||
name: 'vendor/full-provider',
|
||||
tools: [
|
||||
createToolDefinition({ name: 'op_a' }),
|
||||
createToolDefinition({ name: 'op_b' }),
|
||||
],
|
||||
}),
|
||||
]
|
||||
|
||||
// App attaches both operations
|
||||
renderAgentTools([
|
||||
createAgentTool({
|
||||
provider_id: 'full-provider',
|
||||
provider_name: 'vendor/full-provider',
|
||||
tool_name: 'op_a',
|
||||
tool_label: 'Operation A',
|
||||
}),
|
||||
createAgentTool({
|
||||
provider_id: 'full-provider',
|
||||
provider_name: 'vendor/full-provider',
|
||||
tool_name: 'op_b',
|
||||
tool_label: 'Operation B',
|
||||
}),
|
||||
])
|
||||
|
||||
const warningIcon = screen.queryByTestId('tool-ops-out-of-sync')
|
||||
expect(warningIcon).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not show out-of-sync warning when provider has only one operation', () => {
|
||||
// Provider has exactly 1 tool
|
||||
builtInTools = [
|
||||
createCollection({
|
||||
id: 'single-op-provider',
|
||||
name: 'vendor/single-op-provider',
|
||||
tools: [
|
||||
createToolDefinition({ name: 'only_op' }),
|
||||
],
|
||||
}),
|
||||
]
|
||||
|
||||
// App attaches that single operation
|
||||
renderAgentTools([
|
||||
createAgentTool({
|
||||
provider_id: 'single-op-provider',
|
||||
provider_name: 'vendor/single-op-provider',
|
||||
tool_name: 'only_op',
|
||||
tool_label: 'Only Operation',
|
||||
}),
|
||||
])
|
||||
|
||||
const warningIcon = screen.queryByTestId('tool-ops-out-of-sync')
|
||||
expect(warningIcon).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -12,6 +12,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/too
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiEqualizer2Line,
|
||||
RiErrorWarningLine,
|
||||
RiInformation2Line,
|
||||
} from '@remixicon/react'
|
||||
import copy from 'copy-to-clipboard'
|
||||
@ -72,6 +73,35 @@ const AgentTools: FC = () => {
|
||||
collection,
|
||||
}
|
||||
})
|
||||
|
||||
// Detect providers where the app uses fewer operations than the provider exposes
|
||||
const outOfSyncProviders = useMemo(() => {
|
||||
const agentTools = modelConfig?.agentConfig?.tools as AgentTool[] || []
|
||||
const syncMap = new Map<string, { attached: number, available: number }>()
|
||||
|
||||
// Group attached tools by provider_id
|
||||
const attachedByProvider = new Map<string, Set<string>>()
|
||||
for (const tool of agentTools) {
|
||||
if (!attachedByProvider.has(tool.provider_id))
|
||||
attachedByProvider.set(tool.provider_id, new Set())
|
||||
attachedByProvider.get(tool.provider_id)!.add(tool.tool_name)
|
||||
}
|
||||
|
||||
// Compare against available operations in collectionList
|
||||
for (const [providerId, attachedNames] of attachedByProvider) {
|
||||
const collection = collectionList.find(c =>
|
||||
canFindTool(c.id, providerId),
|
||||
)
|
||||
if (collection && collection.tools.length > attachedNames.size) {
|
||||
syncMap.set(providerId, {
|
||||
attached: attachedNames.size,
|
||||
available: collection.tools.length,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return syncMap
|
||||
}, [modelConfig?.agentConfig?.tools, collectionList])
|
||||
const useSubscribe = useMittContextSelector(s => s.useSubscribe)
|
||||
const handleUpdateToolsWhenInstallToolSuccess = useCallback((installedPluginNames: string[]) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
@ -259,6 +289,36 @@ const AgentTools: FC = () => {
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
{!item.isDeleted && outOfSyncProviders.has(item.provider_id) && (
|
||||
<div className="ml-1 flex shrink-0 items-center">
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
aria-label={t('toolOperationsOutOfSync', {
|
||||
ns: 'tools',
|
||||
available: outOfSyncProviders.get(item.provider_id)!.available,
|
||||
attached: outOfSyncProviders.get(item.provider_id)!.attached,
|
||||
})}
|
||||
render={(
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-4 w-4 items-center justify-center rounded-sm outline-hidden hover:bg-black/5 focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
data-testid="tool-ops-out-of-sync"
|
||||
>
|
||||
<RiErrorWarningLine className="h-4 w-4 text-[#F79009]" />
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent popupClassName="w-[240px] px-3 py-2 system-xs-regular text-text-tertiary">
|
||||
{t('toolOperationsOutOfSync', {
|
||||
ns: 'tools',
|
||||
available: outOfSyncProviders.get(item.provider_id)!.available,
|
||||
attached: outOfSyncProviders.get(item.provider_id)!.attached,
|
||||
})}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1 flex shrink-0 items-center">
|
||||
{item.isDeleted && (
|
||||
|
||||
@ -206,6 +206,7 @@
|
||||
"thought.using": "Using",
|
||||
"title": "Tools",
|
||||
"toolNameUsageTip": "Tool call name for agent reasoning and prompting",
|
||||
"toolOperationsOutOfSync": "This tool has {{available}} operations available, but this app only uses {{attached}}. Add missing operations with the '+' button.",
|
||||
"toolRemoved": "Tool removed",
|
||||
"type.builtIn": "Tools",
|
||||
"type.custom": "Custom",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user