@@ -39,7 +39,7 @@ import { buildAPIUrl, buildAuthHeaders } from '@/executor/utils/http'
3939import { stringifyJSON } from '@/executor/utils/json'
4040import { resolveVertexCredential } from '@/executor/utils/vertex-credential'
4141import { executeProviderRequest } from '@/providers'
42- import { getAttachmentProvider , getProviderAttachmentMaxBytes } from '@/providers/attachments'
42+ import { getProviderAttachmentMaxBytes , supportsFileAttachments } from '@/providers/attachments'
4343import { getProviderFromModel , transformBlockTool } from '@/providers/utils'
4444import type { SerializedBlock } from '@/serializer/types'
4545import { filterSchemaForLLM , type ToolSchema } from '@/tools/params'
@@ -91,10 +91,14 @@ export class AgentBlockHandler implements BlockHandler {
9191
9292 const streamingConfig = this . getStreamingConfig ( ctx , block )
9393 const messages = await this . buildMessages ( ctx , filteredInputs , skillMetadata )
94- const messagesWithFiles = await this . attachFilesToLastUserMessage (
94+ const messagesWithInputFiles = this . attachFilesToLastUserMessage (
9595 ctx ,
9696 messages ,
97- filteredInputs . files ,
97+ filteredInputs . files
98+ )
99+ const messagesWithFiles = await this . hydrateMessageFilesForProvider (
100+ ctx ,
101+ messagesWithInputFiles ,
98102 providerId
99103 )
100104
@@ -682,12 +686,11 @@ export class AgentBlockHandler implements BlockHandler {
682686 return messages . length > 0 ? messages : undefined
683687 }
684688
685- private async attachFilesToLastUserMessage (
689+ private attachFilesToLastUserMessage (
686690 ctx : ExecutionContext ,
687691 messages : Message [ ] | undefined ,
688- filesInput : unknown ,
689- providerId : string
690- ) : Promise < Message [ ] | undefined > {
692+ filesInput : unknown
693+ ) : Message [ ] | undefined {
691694 const normalizedFiles = normalizeFileInput ( filesInput )
692695 if ( ! normalizedFiles || normalizedFiles . length === 0 ) {
693696 return messages
@@ -697,10 +700,6 @@ export class AgentBlockHandler implements BlockHandler {
697700 throw new Error ( 'Files require at least one user message in the agent prompt' )
698701 }
699702
700- if ( ! getAttachmentProvider ( providerId ) ) {
701- throw new Error ( `File attachments are not supported for provider "${ providerId } "` )
702- }
703-
704703 let lastUserMessageIndex = - 1
705704 for ( let index = messages . length - 1 ; index >= 0 ; index -- ) {
706705 if ( messages [ index ] . role === 'user' ) {
@@ -718,21 +717,71 @@ export class AgentBlockHandler implements BlockHandler {
718717 throw new Error ( 'Files must include at least one valid file object' )
719718 }
720719
721- const hydratedFiles = await hydrateUserFilesWithBase64 ( userFiles , {
722- requestId,
723- workspaceId : ctx . workspaceId ,
724- workflowId : ctx . workflowId ,
725- executionId : ctx . executionId ,
726- userId : ctx . userId ,
727- logger,
728- maxBytes : getProviderAttachmentMaxBytes ( providerId ) ,
729- } )
730-
731720 const lastUserMessage = messages [ lastUserMessageIndex ]
732721 const nextMessages = [ ...messages ]
733722 nextMessages [ lastUserMessageIndex ] = {
734723 ...lastUserMessage ,
735- files : [ ...( lastUserMessage . files ?? [ ] ) , ...hydratedFiles ] ,
724+ files : [ ...( lastUserMessage . files ?? [ ] ) , ...userFiles ] ,
725+ }
726+
727+ return nextMessages
728+ }
729+
730+ private async hydrateMessageFilesForProvider (
731+ ctx : ExecutionContext ,
732+ messages : Message [ ] | undefined ,
733+ providerId : string
734+ ) : Promise < Message [ ] | undefined > {
735+ if ( ! messages ?. some ( ( message ) => message . files ?. length ) ) {
736+ return messages
737+ }
738+
739+ if ( ! supportsFileAttachments ( providerId ) ) {
740+ throw new Error ( `File attachments are not supported for provider "${ providerId } "` )
741+ }
742+
743+ const requestId = ctx . executionId || ctx . workflowId || 'agent-files'
744+ const nextMessages = [ ...messages ]
745+
746+ for ( let messageIndex = 0 ; messageIndex < messages . length ; messageIndex ++ ) {
747+ const message = messages [ messageIndex ]
748+ const normalizedFiles = normalizeFileInput ( message . files )
749+ if ( ! normalizedFiles || normalizedFiles . length === 0 ) {
750+ continue
751+ }
752+
753+ const userFiles = processFilesToUserFiles (
754+ normalizedFiles as RawFileInput [ ] ,
755+ requestId ,
756+ logger
757+ )
758+ if ( userFiles . length === 0 ) {
759+ throw new Error ( 'Files must include at least one valid file object' )
760+ }
761+
762+ const hydratedFiles = await hydrateUserFilesWithBase64 ( userFiles , {
763+ requestId,
764+ workspaceId : ctx . workspaceId ,
765+ workflowId : ctx . workflowId ,
766+ executionId : ctx . executionId ,
767+ largeValueExecutionIds : ctx . largeValueExecutionIds ,
768+ allowLargeValueWorkflowScope : ctx . allowLargeValueWorkflowScope ,
769+ userId : ctx . userId ,
770+ logger,
771+ maxBytes : getProviderAttachmentMaxBytes ( providerId ) ,
772+ } )
773+
774+ const missingFile = hydratedFiles . find ( ( file ) => ! file . base64 )
775+ if ( missingFile ) {
776+ throw new Error (
777+ `File "${ missingFile . name } " could not be read for provider "${ providerId } ". Make sure the file is still accessible and under the provider attachment size limit.`
778+ )
779+ }
780+
781+ nextMessages [ messageIndex ] = {
782+ ...message ,
783+ files : hydratedFiles ,
784+ }
736785 }
737786
738787 return nextMessages
0 commit comments