fix(agent-v2): stop dropping non-vision attachments from agent chat (#40179)

Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
This commit is contained in:
chelsealong 2026-08-12 13:57:21 +08:00 committed by GitHub
parent 9041251da5
commit d813edb945
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 25 deletions

View File

@ -1,6 +1,7 @@
import type { ComponentProps, ReactNode } from 'react'
import type { AgentPreviewChatController } from '../chat-conversation'
import type { AgentChatRuntimeEmptyStateProps } from '../chat-runtime'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { SpeechToTextTarget } from '@/app/components/base/voice-input/types'
import type { AgentSoulConfigFormState } from '@/features/agent-v2/agent-composer/form-state'
import { toast } from '@langgenius/dify-ui/toast'
@ -57,7 +58,7 @@ vi.mock('@/next/dynamic', async () => {
default: () =>
function MockChat(props: {
answerActionPosition?: 'auto' | 'below'
onSend: (message: string) => unknown
onSend: (message: string, files?: FileEntity[]) => unknown
onStopResponding: () => void
sendButtonLabel?: string
sendButtonLoading?: boolean
@ -113,6 +114,26 @@ vi.mock('@/next/dynamic', async () => {
>
send
</button>
<button
type="button"
onClick={() => {
setSent(true)
sendResultRef.current = props.onSend('read this file', [
{
id: 'file-1',
name: 'brief.docx',
size: 1024,
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
progress: 100,
transferMethod: TransferMethod.local_file,
supportFileType: 'document',
uploadedId: 'uploaded-file-1',
},
])
}}
>
send with document
</button>
<button type="button" onClick={props.onStopResponding}>
stop
</button>
@ -720,6 +741,21 @@ describe('AgentPreviewChat', () => {
)
})
it('should deliver non-image attachments even when the selected model does not support vision', async () => {
renderPreviewChat()
fireEvent.click(screen.getByRole('button', { name: 'send with document' }))
await waitFor(() => expect(handleSendMock).toHaveBeenCalledTimes(1))
expect(handleSendMock).toHaveBeenCalledWith(
'agent/agent-1/chat-messages',
expect.objectContaining({
files: [expect.objectContaining({ id: 'file-1', name: 'brief.docx' })],
}),
expect.any(Object),
)
})
it('should save draft before sending preview chat through the agent chat endpoints', async () => {
const saveDraftBeforeRun = vi.fn().mockResolvedValue(undefined)
renderPreviewChat({

View File

@ -19,13 +19,11 @@ import { useTranslation } from 'react-i18next'
import { AgentRosterResponseContent } from '@/app/components/base/chat/chat/answer/agent-roster-response-content'
import { useChat } from '@/app/components/base/chat/chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { useDocLink } from '@/context/i18n'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import dynamic from '@/next/dynamic'
import { consoleClient, consoleQuery } from '@/service/client'
import { buildChatConfig, getAgentSoulInputs, getAgentSoulInputsForm } from './chat-config'
import { getAgentSoulInputs, getAgentSoulInputsForm } from './chat-config'
const Chat = dynamic(() => import('@/app/components/base/chat/chat'), { ssr: false })
@ -79,7 +77,7 @@ export function AgentPreviewChatConversation({
clearChatList,
config,
conversationId,
currentModel,
currentModel: _currentModel,
draftType,
initialChatTree,
inputs,
@ -135,8 +133,6 @@ export function AgentPreviewChatConversation({
sendInterruptedRef.current = true
onSendInterrupted?.()
}, [onSendInterrupted])
const { textGenerationModelList } =
useTextGenerationCurrentProviderAndModelAndModelList(currentModel)
const {
chatList,
setTargetMessageId,
@ -178,21 +174,6 @@ export function AgentPreviewChatConversation({
const runtimeInputs = preparedAgentSoulConfig
? getAgentSoulInputs(runtimeInputsForm)
: inputs
const runtimeConfig = preparedAgentSoulConfig
? buildChatConfig({
agentSoulConfig: runtimeAgentSoulConfig,
currentModel: undefined,
prompt: runtimeAgentSoulConfig?.prompt?.system_prompt ?? '',
})
: config
const currentProvider = textGenerationModelList.find(
(item) => item.provider === runtimeConfig.model.provider,
)
const selectedModel = currentProvider?.models.find(
(model) => model.model === runtimeConfig.model.name,
)
const supportVision = selectedModel?.features?.includes(ModelFeatureEnum.vision)
const data: Record<string, unknown> = {
query: message,
inputs: runtimeInputs,
@ -202,7 +183,7 @@ export function AgentPreviewChatConversation({
}
if (draftType) data.draft_type = draftType
if (files?.length && supportVision) data.files = files
if (files?.length) data.files = files
sendMessage({
agentId,
@ -280,7 +261,6 @@ export function AgentPreviewChatConversation({
agentId,
agentSoulConfig,
chatList,
config,
conversationId,
draftType,
docLink,
@ -295,7 +275,6 @@ export function AgentPreviewChatConversation({
queryClient,
sendMessage,
t,
textGenerationModelList,
],
)