fix(ai-chat) - upload files (#20681)

closes https://github.com/twentyhq/twenty/issues/20437

bonus : persist file filename for UI display
This commit is contained in:
Etienne
2026-05-18 17:50:02 +02:00
committed by GitHub
parent 132d997474
commit 89579f5225
11 changed files with 160 additions and 91 deletions
@@ -3263,7 +3263,7 @@ type Mutation {
updateWebhook(input: UpdateWebhookInput!): Webhook!
deleteWebhook(id: UUID!): Webhook!
createChatThread: AgentChatThread!
sendChatMessage(threadId: UUID!, text: String!, messageId: UUID!, browsingContext: JSON, modelId: String, fileIds: [UUID!]): SendChatMessageResult!
sendChatMessage(threadId: UUID!, text: String!, messageId: UUID!, browsingContext: JSON, modelId: String, fileAttachments: [FileAttachmentInput!]): SendChatMessageResult!
stopAgentChatStream(threadId: UUID!): Boolean!
renameChatThread(id: UUID!, title: String!): AgentChatThread!
archiveChatThread(id: UUID!): AgentChatThread!
@@ -4228,6 +4228,11 @@ input UpdateWebhookInputUpdates {
secret: String
}
input FileAttachmentInput {
id: UUID!
filename: String!
}
input CreateSkillInput {
id: UUID
name: String!
@@ -5872,7 +5872,7 @@ export interface MutationGenqlSelection{
updateWebhook?: (WebhookGenqlSelection & { __args: {input: UpdateWebhookInput} })
deleteWebhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} })
createChatThread?: AgentChatThreadGenqlSelection
sendChatMessage?: (SendChatMessageResultGenqlSelection & { __args: {threadId: Scalars['UUID'], text: Scalars['String'], messageId: Scalars['UUID'], browsingContext?: (Scalars['JSON'] | null), modelId?: (Scalars['String'] | null), fileIds?: (Scalars['UUID'][] | null)} })
sendChatMessage?: (SendChatMessageResultGenqlSelection & { __args: {threadId: Scalars['UUID'], text: Scalars['String'], messageId: Scalars['UUID'], browsingContext?: (Scalars['JSON'] | null), modelId?: (Scalars['String'] | null), fileAttachments?: (FileAttachmentInput[] | null)} })
stopAgentChatStream?: { __args: {threadId: Scalars['UUID']} }
renameChatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID'], title: Scalars['String']} })
archiveChatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID']} })
@@ -6259,6 +6259,8 @@ update: UpdateWebhookInputUpdates}
export interface UpdateWebhookInputUpdates {targetUrl?: (Scalars['String'] | null),operations?: (Scalars['String'][] | null),description?: (Scalars['String'] | null),secret?: (Scalars['String'] | null)}
export interface FileAttachmentInput {id: Scalars['UUID'],filename: Scalars['String']}
export interface CreateSkillInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],label: Scalars['String'],icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),content: Scalars['String']}
export interface UpdateSkillInput {id: Scalars['UUID'],name?: (Scalars['String'] | null),label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),content?: (Scalars['String'] | null),isActive?: (Scalars['Boolean'] | null)}
@@ -78,8 +78,8 @@ export default {
335,
342,
378,
454,
466
455,
467
],
"types": {
"BillingProductDTO": {
@@ -7876,9 +7876,9 @@ export default {
"modelId": [
1
],
"fileIds": [
3,
"[UUID!]"
"fileAttachments": [
440,
"[FileAttachmentInput!]"
]
}
],
@@ -7944,7 +7944,7 @@ export default {
290,
{
"input": [
440,
441,
"CreateSkillInput!"
]
}
@@ -7953,7 +7953,7 @@ export default {
290,
{
"input": [
441,
442,
"UpdateSkillInput!"
]
}
@@ -8011,7 +8011,7 @@ export default {
238,
{
"input": [
442,
443,
"GetAuthorizationUrlForSSOInput!"
]
}
@@ -8265,7 +8265,7 @@ export default {
196,
{
"input": [
443,
444,
"CreateApplicationRegistrationInput!"
]
}
@@ -8274,7 +8274,7 @@ export default {
7,
{
"input": [
444,
445,
"UpdateApplicationRegistrationInput!"
]
}
@@ -8301,7 +8301,7 @@ export default {
5,
{
"input": [
446,
447,
"CreateApplicationRegistrationVariableInput!"
]
}
@@ -8310,7 +8310,7 @@ export default {
5,
{
"input": [
447,
448,
"UpdateApplicationRegistrationVariableInput!"
]
}
@@ -8399,7 +8399,7 @@ export default {
6,
{
"input": [
449,
450,
"UpdateWorkspaceMemberSettingsInput!"
]
}
@@ -8433,7 +8433,7 @@ export default {
75,
{
"data": [
450,
451,
"ActivateWorkspaceInput!"
]
}
@@ -8442,7 +8442,7 @@ export default {
75,
{
"data": [
451,
452,
"UpdateWorkspaceInput!"
]
}
@@ -8469,7 +8469,7 @@ export default {
6,
{
"workspaceMigration": [
452,
453,
"WorkspaceMigrationInput!"
]
}
@@ -8487,7 +8487,7 @@ export default {
220,
{
"input": [
455,
456,
"SetupOIDCSsoInput!"
]
}
@@ -8496,7 +8496,7 @@ export default {
220,
{
"input": [
456,
457,
"SetupSAMLSsoInput!"
]
}
@@ -8505,7 +8505,7 @@ export default {
216,
{
"input": [
457,
458,
"DeleteSsoInput!"
]
}
@@ -8514,7 +8514,7 @@ export default {
217,
{
"input": [
458,
459,
"EditSsoInput!"
]
}
@@ -8545,7 +8545,7 @@ export default {
286,
{
"input": [
459,
460,
"SendEmailInput!"
]
}
@@ -8571,7 +8571,7 @@ export default {
"String!"
],
"connectionParameters": [
461,
462,
"EmailAccountConnectionParameters!"
],
"id": [
@@ -8583,7 +8583,7 @@ export default {
168,
{
"input": [
463,
464,
"UpdateLabPublicFeatureFlagInput!"
]
}
@@ -8665,7 +8665,7 @@ export default {
77,
{
"input": [
464,
465,
"CreateOneAppTokenInput!"
]
}
@@ -8728,7 +8728,7 @@ export default {
"String!"
],
"fileFolder": [
466,
467,
"FileFolder!"
],
"filePath": [
@@ -10746,6 +10746,17 @@ export default {
1
]
},
"FileAttachmentInput": {
"id": [
3
],
"filename": [
1
],
"__typename": [
1
]
},
"CreateSkillInput": {
"id": [
3
@@ -10828,7 +10839,7 @@ export default {
1
],
"update": [
445
446
],
"__typename": [
1
@@ -10876,7 +10887,7 @@ export default {
1
],
"update": [
448
449
],
"__typename": [
1
@@ -10994,7 +11005,7 @@ export default {
},
"WorkspaceMigrationInput": {
"actions": [
453
454
],
"__typename": [
1
@@ -11002,7 +11013,7 @@ export default {
},
"WorkspaceMigrationDeleteActionInput": {
"type": [
454
455
],
"metadataName": [
318
@@ -11097,7 +11108,7 @@ export default {
1
],
"files": [
460
461
],
"__typename": [
1
@@ -11116,13 +11127,13 @@ export default {
},
"EmailAccountConnectionParameters": {
"IMAP": [
462
463
],
"SMTP": [
462
463
],
"CALDAV": [
462
463
],
"__typename": [
1
@@ -11161,7 +11172,7 @@ export default {
},
"CreateOneAppTokenInput": {
"appToken": [
465
466
],
"__typename": [
1
@@ -11190,7 +11201,7 @@ export default {
230,
{
"input": [
468,
469,
"LogicFunctionLogsInput!"
]
}
@@ -1792,6 +1792,11 @@ export type File = {
size: Scalars['Float'];
};
export type FileAttachmentInput = {
filename: Scalars['String'];
id: Scalars['UUID'];
};
export enum FileFolder {
AgentChat = 'AgentChat',
AppTarball = 'AppTarball',
@@ -3197,7 +3202,7 @@ export type MutationSaveImapSmtpCaldavAccountArgs = {
export type MutationSendChatMessageArgs = {
browsingContext?: InputMaybe<Scalars['JSON']>;
fileIds?: InputMaybe<Array<Scalars['UUID']>>;
fileAttachments?: InputMaybe<Array<FileAttachmentInput>>;
messageId: Scalars['UUID'];
modelId?: InputMaybe<Scalars['String']>;
text: Scalars['String'];
@@ -6148,7 +6153,7 @@ export type SendChatMessageMutationVariables = Exact<{
messageId: Scalars['UUID'];
browsingContext?: InputMaybe<Scalars['JSON']>;
modelId?: InputMaybe<Scalars['String']>;
fileIds?: InputMaybe<Array<Scalars['UUID']> | Scalars['UUID']>;
fileAttachments?: InputMaybe<Array<FileAttachmentInput> | FileAttachmentInput>;
}>;
@@ -7983,7 +7988,7 @@ export const EvaluateAgentTurnDocument = {"kind":"Document","definitions":[{"kin
export const RemoveRoleFromAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemoveRoleFromAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"removeRoleFromAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}}]}]}}]} as unknown as DocumentNode<RemoveRoleFromAgentMutation, RemoveRoleFromAgentMutationVariables>;
export const RenameChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RenameChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"title"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"renameChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"title"},"value":{"kind":"Variable","name":{"kind":"Name","value":"title"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<RenameChatThreadMutation, RenameChatThreadMutationVariables>;
export const RunEvaluationInputDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RunEvaluationInput"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runEvaluationInput"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"agentId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"evaluations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode<RunEvaluationInputMutation, RunEvaluationInputMutationVariables>;
export const SendChatMessageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendChatMessage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"browsingContext"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fileIds"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendChatMessage"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}},{"kind":"Argument","name":{"kind":"Name","value":"text"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}},{"kind":"Argument","name":{"kind":"Name","value":"messageId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}}},{"kind":"Argument","name":{"kind":"Name","value":"browsingContext"},"value":{"kind":"Variable","name":{"kind":"Name","value":"browsingContext"}}},{"kind":"Argument","name":{"kind":"Name","value":"modelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"fileIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fileIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"messageId"}},{"kind":"Field","name":{"kind":"Name","value":"queued"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}}]}}]}}]} as unknown as DocumentNode<SendChatMessageMutation, SendChatMessageMutationVariables>;
export const SendChatMessageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendChatMessage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"browsingContext"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fileAttachments"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FileAttachmentInput"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendChatMessage"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}},{"kind":"Argument","name":{"kind":"Name","value":"text"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}},{"kind":"Argument","name":{"kind":"Name","value":"messageId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}}},{"kind":"Argument","name":{"kind":"Name","value":"browsingContext"},"value":{"kind":"Variable","name":{"kind":"Name","value":"browsingContext"}}},{"kind":"Argument","name":{"kind":"Name","value":"modelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"fileAttachments"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fileAttachments"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"messageId"}},{"kind":"Field","name":{"kind":"Name","value":"queued"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}}]}}]}}]} as unknown as DocumentNode<SendChatMessageMutation, SendChatMessageMutationVariables>;
export const StopAgentChatStreamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StopAgentChatStream"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stopAgentChatStream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}]}]}}]} as unknown as DocumentNode<StopAgentChatStreamMutation, StopAgentChatStreamMutationVariables>;
export const UnarchiveChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UnarchiveChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unarchiveChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<UnarchiveChatThreadMutation, UnarchiveChatThreadMutationVariables>;
export const UpdateOneAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateAgentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AgentFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AgentFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Agent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"prompt"}},{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"responseFormat"}},{"kind":"Field","name":{"kind":"Name","value":"roleId"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"modelConfiguration"}},{"kind":"Field","name":{"kind":"Name","value":"evaluationInputs"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<UpdateOneAgentMutation, UpdateOneAgentMutationVariables>;
@@ -7,7 +7,7 @@ export const SEND_CHAT_MESSAGE = gql`
$messageId: UUID!
$browsingContext: JSON
$modelId: String
$fileIds: [UUID!]
$fileAttachments: [FileAttachmentInput!]
) {
sendChatMessage(
threadId: $threadId
@@ -15,7 +15,7 @@ export const SEND_CHAT_MESSAGE = gql`
messageId: $messageId
browsingContext: $browsingContext
modelId: $modelId
fileIds: $fileIds
fileAttachments: $fileAttachments
) {
messageId
queued
@@ -30,7 +30,6 @@ import { useListenToBrowserEvent } from '@/browser-event/hooks/useListenToBrowse
import { dispatchBrowserEvent } from '@/browser-event/utils/dispatchBrowserEvent';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
export const useAgentChat = (
@@ -44,11 +43,9 @@ export const useAgentChat = (
const setCurrentAiChatThread = useSetAtomState(currentAiChatThreadState);
const store = useStore();
const agentChatSelectedFiles = useAtomStateValue(agentChatSelectedFilesState);
const [, setPendingThreadIdAfterFirstSend] = useState<string | null>(null);
const [agentChatUploadedFiles, setAgentChatUploadedFiles] = useAtomState(
const setAgentChatUploadedFiles = useSetAtomState(
agentChatUploadedFilesState,
);
@@ -74,12 +71,14 @@ export const useAgentChat = (
return;
}
const isLoading = agentChatSelectedFiles.length > 0;
const agentChatSelectedFiles = store.get(agentChatSelectedFilesState.atom);
if (isLoading) {
if (agentChatSelectedFiles.length > 0) {
return;
}
const agentChatUploadedFiles = store.get(agentChatUploadedFilesState.atom);
const threadId = await ensureThreadIdForSend();
if (!isDefined(threadId)) {
@@ -131,7 +130,11 @@ export const useAgentChat = (
store.set(messagesAtom, [...currentMessages, optimisticUserMessage]);
store.set(errorAtom, null);
const fileIds = agentChatUploadedFiles.map((file) => file.fileId);
const fileAttachments = agentChatUploadedFiles.map((file) => ({
id: file.fileId,
filename: file.filename,
}));
const uploadedFilesSnapshot = agentChatUploadedFiles;
setAgentChatUploadedFiles([]);
@@ -150,7 +153,8 @@ export const useAgentChat = (
messageId,
browsingContext: browsingContext ?? null,
modelId: modelIdForRequest ?? undefined,
fileIds: fileIds.length > 0 ? fileIds : undefined,
fileAttachments:
fileAttachments.length > 0 ? fileAttachments : undefined,
},
});
@@ -186,6 +190,7 @@ export const useAgentChat = (
? { [AGENT_CHAT_NEW_THREAD_DRAFT_KEY]: '' }
: {}),
}));
setAgentChatUploadedFiles(uploadedFilesSnapshot);
const latestMessages = store.get(messagesAtom);
@@ -214,11 +219,9 @@ export const useAgentChat = (
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
store,
agentChatSelectedFiles,
ensureThreadIdForSend,
setAgentChatInput,
getBrowsingContext,
agentChatUploadedFiles,
setAgentChatUploadedFiles,
setAgentChatDraftsByThreadId,
modelIdForRequest,
@@ -0,0 +1,12 @@
import { Field, InputType } from '@nestjs/graphql';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
@InputType()
export class FileAttachmentInput {
@Field(() => UUIDScalarType)
id: string;
@Field()
filename: string;
}
@@ -27,6 +27,7 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { AgentMessageDTO } from 'src/engine/metadata-modules/ai/ai-agent-execution/dtos/agent-message.dto';
import { type BrowsingContextType } from 'src/engine/metadata-modules/ai/ai-agent/types/browsingContext.type';
import { AgentChatThreadDTO } from 'src/engine/metadata-modules/ai/ai-chat/dtos/agent-chat-thread.dto';
import { FileAttachmentInput } from 'src/engine/metadata-modules/ai/ai-chat/dtos/file-attachment.input';
import { AiSystemPromptPreviewDTO } from 'src/engine/metadata-modules/ai/ai-chat/dtos/ai-system-prompt-preview.dto';
import { ChatStreamCatchupChunksDTO } from 'src/engine/metadata-modules/ai/ai-chat/dtos/chat-stream-catchup-chunks.dto';
import { SendChatMessageResultDTO } from 'src/engine/metadata-modules/ai/ai-chat/dtos/send-chat-message-result.dto';
@@ -113,8 +114,11 @@ export class AgentChatResolver {
browsingContext: BrowsingContextType | null,
@Args('modelId', { type: () => String, nullable: true })
modelId: string | undefined,
@Args('fileIds', { type: () => [UUIDScalarType], nullable: true })
fileIds: string[] | null,
@Args('fileAttachments', {
type: () => [FileAttachmentInput],
nullable: true,
})
fileAttachments: FileAttachmentInput[] | null,
@AuthUserWorkspaceId() userWorkspaceId: string,
@AuthWorkspace() workspace: WorkspaceEntity,
): Promise<SendChatMessageResultDTO> {
@@ -157,7 +161,7 @@ export class AgentChatResolver {
threadId,
text,
id: messageId,
fileIds: fileIds ?? undefined,
fileAttachments: fileAttachments ?? undefined,
workspaceId: workspace.id,
userWorkspaceId,
});
@@ -179,7 +183,7 @@ export class AgentChatResolver {
workspace,
text,
messageId,
fileIds: fileIds ?? undefined,
fileAttachments: fileAttachments ?? undefined,
});
return {
@@ -21,18 +21,19 @@ import {
AgentMessageStatus,
} from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity';
import { mapDBPartsToUIMessageParts } from 'src/engine/metadata-modules/ai/ai-agent-execution/utils/mapDBPartsToUIMessageParts';
import {
AiException,
AiExceptionCode,
} from 'src/engine/metadata-modules/ai/ai.exception';
import { type BrowsingContextType } from 'src/engine/metadata-modules/ai/ai-agent/types/browsingContext.type';
import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity';
import { STREAM_AGENT_CHAT_JOB_NAME } from 'src/engine/metadata-modules/ai/ai-chat/jobs/stream-agent-chat-job-name.constant';
import { type StreamAgentChatJobData } from 'src/engine/metadata-modules/ai/ai-chat/jobs/stream-agent-chat-job.types';
import { AgentChatEventPublisherService } from 'src/engine/metadata-modules/ai/ai-chat/services/agent-chat-event-publisher.service';
import { AgentChatService } from 'src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service';
import { AiChatFileAttachment } from 'src/engine/metadata-modules/ai/ai-chat/types/ai-chat-file-attachment.type';
import {
AiException,
AiExceptionCode,
} from 'src/engine/metadata-modules/ai/ai.exception';
export type StreamAgentChatOptions = {
type StreamAgentChatOptions = {
threadId: string;
userWorkspaceId: string;
workspace: WorkspaceEntity;
@@ -40,7 +41,7 @@ export type StreamAgentChatOptions = {
browsingContext: BrowsingContextType | null;
modelId?: string;
messageId?: string;
fileIds?: string[];
fileAttachments?: AiChatFileAttachment[];
};
@Injectable()
@@ -67,7 +68,7 @@ export class AgentChatStreamingService {
browsingContext,
modelId,
messageId,
fileIds,
fileAttachments,
}: StreamAgentChatOptions): Promise<{ streamId: string; messageId: string }> {
const thread = await this.threadRepository.findOne({
where: {
@@ -83,7 +84,10 @@ export class AgentChatStreamingService {
);
}
const fileParts = await this.buildFilePartsFromIds(fileIds, workspace.id);
const fileParts = await this.buildFilePartsFromAttachments(
fileAttachments,
workspace.id,
);
const userMessageParts: ExtendedUIMessagePart[] = [
{ type: 'text' as const, text },
@@ -281,30 +285,40 @@ export class AgentChatStreamingService {
);
}
private async buildFilePartsFromIds(
fileIds: string[] | undefined,
private async buildFilePartsFromAttachments(
fileAttachments: AiChatFileAttachment[] | undefined,
workspaceId: string,
): Promise<ExtendedUIMessagePart[]> {
if (!fileIds || fileIds.length === 0) {
if (!fileAttachments || fileAttachments.length === 0) {
return [];
}
const files = await this.fileRepository.find({
const fileIds = fileAttachments.map((attachment) => attachment.id);
const validFiles = await this.fileRepository.find({
where: {
id: In(fileIds),
workspaceId,
path: Like(`%/${FileFolder.AgentChat}/%`),
path: Like(`${FileFolder.AgentChat}/%`),
},
});
return files.map(
(file): ExtendedFileUIPart => ({
type: 'file' as const,
mediaType: file.mimeType,
filename: file.path.split('/').pop() ?? file.path,
url: '',
fileId: file.id,
}),
);
const validFileIds = new Set(validFiles.map((file) => file.id));
return fileAttachments
.filter((attachment) => validFileIds.has(attachment.id))
.map((attachment): ExtendedFileUIPart => {
const file = validFiles.find(
(validFile) => validFile.id === attachment.id,
);
return {
type: 'file' as const,
mediaType: file?.mimeType ?? 'application/octet-stream',
filename: attachment.filename,
url: '',
fileId: attachment.id,
};
});
}
}
@@ -23,6 +23,7 @@ import {
} from 'src/engine/metadata-modules/ai/ai.exception';
import { WorkspaceEventBroadcaster } from 'src/engine/subscriptions/workspace-event-broadcaster/workspace-event-broadcaster.service';
import { AiChatFileAttachment } from 'src/engine/metadata-modules/ai/ai-chat/types/ai-chat-file-attachment.type';
import { AgentTitleGenerationService } from './agent-title-generation.service';
const serializeThreadForBroadcast = (
@@ -252,14 +253,14 @@ export class AgentChatService {
threadId,
text,
id,
fileIds,
fileAttachments,
workspaceId,
userWorkspaceId,
}: {
threadId: string;
text: string;
id?: string;
fileIds?: string[];
fileAttachments?: AiChatFileAttachment[];
workspaceId: string;
userWorkspaceId: string;
}): Promise<AgentMessageEntity> {
@@ -277,13 +278,19 @@ export class AgentChatService {
const savedMessageId = (id ?? insertResult.identifiers[0].id) as string;
const files =
fileIds && fileIds.length > 0
const validFiles =
fileAttachments && fileAttachments.length > 0
? await this.fileRepository.find({
where: { id: In(fileIds), workspaceId },
where: {
id: In(fileAttachments.map((attachment) => attachment.id)),
workspaceId,
},
select: ['id'],
})
: [];
const validFileIds = new Set(validFiles.map((file) => file.id));
const parts = [
{
messageId: savedMessageId,
@@ -292,14 +299,16 @@ export class AgentChatService {
textContent: text,
workspaceId,
},
...files.map((file, index) => ({
messageId: savedMessageId,
orderIndex: index + 1,
type: 'file',
fileId: file.id,
fileFilename: file.path.split('/').pop() ?? null,
workspaceId,
})),
...(fileAttachments ?? [])
.filter((attachment) => validFileIds.has(attachment.id))
.map((attachment, index) => ({
messageId: savedMessageId,
orderIndex: index + 1,
type: 'file',
fileId: attachment.id,
fileFilename: attachment.filename,
workspaceId,
})),
];
await this.messagePartRepository.insert(parts);
@@ -0,0 +1,4 @@
export type AiChatFileAttachment = {
id: string;
filename: string;
};