diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 57be9e923..8e57eca80 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -70,6 +70,7 @@ export type Agent = { name: Scalars['String']['output']; prompt: Scalars['String']['output']; responseFormat?: Maybe; + roleId?: Maybe; updatedAt: Scalars['DateTime']['output']; }; @@ -1003,6 +1004,7 @@ export type Mutation = { __typename?: 'Mutation'; activateWorkflowVersion: Scalars['Boolean']['output']; activateWorkspace: Workspace; + assignRoleToAgent: Scalars['Boolean']['output']; authorizeApp: AuthorizeApp; checkCustomDomainValidRecords?: Maybe; checkoutSession: BillingSessionOutput; @@ -1049,6 +1051,7 @@ export type Mutation = { getLoginTokenFromEmailVerificationToken: GetLoginTokenFromEmailVerificationTokenOutput; impersonate: ImpersonateOutput; publishServerlessFunction: ServerlessFunction; + removeRoleFromAgent: Scalars['Boolean']['output']; renewToken: AuthTokens; resendEmailVerificationToken: ResendEmailVerificationTokenOutput; resendWorkspaceInvitation: SendInvitationsOutput; @@ -1103,6 +1106,12 @@ export type MutationActivateWorkspaceArgs = { }; +export type MutationAssignRoleToAgentArgs = { + agentId: Scalars['UUID']['input']; + roleId: Scalars['UUID']['input']; +}; + + export type MutationAuthorizeAppArgs = { clientId: Scalars['String']['input']; codeChallenge?: InputMaybe; @@ -1317,6 +1326,11 @@ export type MutationPublishServerlessFunctionArgs = { }; +export type MutationRemoveRoleFromAgentArgs = { + agentId: Scalars['UUID']['input']; +}; + + export type MutationRenewTokenArgs = { appToken: Scalars['String']['input']; }; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 53e98e9f6..15f5cc988 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -62,6 +62,7 @@ export type Agent = { name: Scalars['String']; prompt: Scalars['String']; responseFormat?: Maybe; + roleId?: Maybe; updatedAt: Scalars['DateTime']; }; @@ -952,6 +953,7 @@ export type Mutation = { __typename?: 'Mutation'; activateWorkflowVersion: Scalars['Boolean']; activateWorkspace: Workspace; + assignRoleToAgent: Scalars['Boolean']; authorizeApp: AuthorizeApp; checkCustomDomainValidRecords?: Maybe; checkoutSession: BillingSessionOutput; @@ -996,6 +998,7 @@ export type Mutation = { getLoginTokenFromEmailVerificationToken: GetLoginTokenFromEmailVerificationTokenOutput; impersonate: ImpersonateOutput; publishServerlessFunction: ServerlessFunction; + removeRoleFromAgent: Scalars['Boolean']; renewToken: AuthTokens; resendEmailVerificationToken: ResendEmailVerificationTokenOutput; resendWorkspaceInvitation: SendInvitationsOutput; @@ -1046,6 +1049,12 @@ export type MutationActivateWorkspaceArgs = { }; +export type MutationAssignRoleToAgentArgs = { + agentId: Scalars['UUID']; + roleId: Scalars['UUID']; +}; + + export type MutationAuthorizeAppArgs = { clientId: Scalars['String']; codeChallenge?: InputMaybe; @@ -1240,6 +1249,11 @@ export type MutationPublishServerlessFunctionArgs = { }; +export type MutationRemoveRoleFromAgentArgs = { + agentId: Scalars['UUID']; +}; + + export type MutationRenewTokenArgs = { appToken: Scalars['String']; }; @@ -3180,6 +3194,21 @@ export type UpdateWorkflowVersionStepMutationVariables = Exact<{ export type UpdateWorkflowVersionStepMutation = { __typename?: 'Mutation', updateWorkflowVersionStep: { __typename?: 'WorkflowAction', id: any, name: string, type: string, settings: any, valid: boolean, nextStepIds?: Array | null } }; +export type AssignRoleToAgentMutationVariables = Exact<{ + agentId: Scalars['UUID']; + roleId: Scalars['UUID']; +}>; + + +export type AssignRoleToAgentMutation = { __typename?: 'Mutation', assignRoleToAgent: boolean }; + +export type RemoveRoleFromAgentMutationVariables = Exact<{ + agentId: Scalars['UUID']; +}>; + + +export type RemoveRoleFromAgentMutation = { __typename?: 'Mutation', removeRoleFromAgent: boolean }; + export type UpdateOneAgentMutationVariables = Exact<{ input: UpdateAgentInput; }>; @@ -3192,7 +3221,7 @@ export type FindOneAgentQueryVariables = Exact<{ }>; -export type FindOneAgentQuery = { __typename?: 'Query', findOneAgent: { __typename?: 'Agent', id: any, name: string, description?: string | null, prompt: string, modelId: string, responseFormat?: any | null } }; +export type FindOneAgentQuery = { __typename?: 'Query', findOneAgent: { __typename?: 'Agent', id: any, name: string, description?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: any | null } }; export type SubmitFormStepMutationVariables = Exact<{ input: SubmitFormStepInput; @@ -6569,6 +6598,69 @@ export function useUpdateWorkflowVersionStepMutation(baseOptions?: Apollo.Mutati export type UpdateWorkflowVersionStepMutationHookResult = ReturnType; export type UpdateWorkflowVersionStepMutationResult = Apollo.MutationResult; export type UpdateWorkflowVersionStepMutationOptions = Apollo.BaseMutationOptions; +export const AssignRoleToAgentDocument = gql` + mutation AssignRoleToAgent($agentId: UUID!, $roleId: UUID!) { + assignRoleToAgent(agentId: $agentId, roleId: $roleId) +} + `; +export type AssignRoleToAgentMutationFn = Apollo.MutationFunction; + +/** + * __useAssignRoleToAgentMutation__ + * + * To run a mutation, you first call `useAssignRoleToAgentMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAssignRoleToAgentMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [assignRoleToAgentMutation, { data, loading, error }] = useAssignRoleToAgentMutation({ + * variables: { + * agentId: // value for 'agentId' + * roleId: // value for 'roleId' + * }, + * }); + */ +export function useAssignRoleToAgentMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AssignRoleToAgentDocument, options); + } +export type AssignRoleToAgentMutationHookResult = ReturnType; +export type AssignRoleToAgentMutationResult = Apollo.MutationResult; +export type AssignRoleToAgentMutationOptions = Apollo.BaseMutationOptions; +export const RemoveRoleFromAgentDocument = gql` + mutation RemoveRoleFromAgent($agentId: UUID!) { + removeRoleFromAgent(agentId: $agentId) +} + `; +export type RemoveRoleFromAgentMutationFn = Apollo.MutationFunction; + +/** + * __useRemoveRoleFromAgentMutation__ + * + * To run a mutation, you first call `useRemoveRoleFromAgentMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useRemoveRoleFromAgentMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [removeRoleFromAgentMutation, { data, loading, error }] = useRemoveRoleFromAgentMutation({ + * variables: { + * agentId: // value for 'agentId' + * }, + * }); + */ +export function useRemoveRoleFromAgentMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(RemoveRoleFromAgentDocument, options); + } +export type RemoveRoleFromAgentMutationHookResult = ReturnType; +export type RemoveRoleFromAgentMutationResult = Apollo.MutationResult; +export type RemoveRoleFromAgentMutationOptions = Apollo.BaseMutationOptions; export const UpdateOneAgentDocument = gql` mutation UpdateOneAgent($input: UpdateAgentInput!) { updateOneAgent(input: $input) { @@ -6616,6 +6708,7 @@ export const FindOneAgentDocument = gql` prompt modelId responseFormat + roleId } } `; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx index 42b15756a..3d3e4bf8f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx @@ -1,5 +1,8 @@ import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput'; import { Select } from '@/ui/input/components/Select'; +import { TabList } from '@/ui/layout/tab-list/components/TabList'; +import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { WorkflowAiAgentAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; @@ -8,8 +11,13 @@ import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components import { BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; -import { useIcons } from 'twenty-ui/display'; +import { IconSettings, IconTool, useIcons } from 'twenty-ui/display'; import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader'; +import { + WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID, + WorkflowAiAgentTabId, +} from '../constants/workflow-ai-agent-tabs'; +import { useAgentRoleAssignment } from '../hooks/useAgentRoleAssignment'; import { useAgentUpdateFormState } from '../hooks/useAgentUpdateFormState'; import { useAiAgentOutputSchema } from '../hooks/useAiAgentOutputSchema'; import { useAiModelOptions } from '../hooks/useAiModelOptions'; @@ -22,6 +30,11 @@ const StyledErrorMessage = styled.div` margin-top: ${({ theme }) => theme.spacing(1)}; `; +const StyledTabList = styled(TabList)` + background-color: ${({ theme }) => theme.background.secondary}; + padding-left: ${({ theme }) => theme.spacing(2)}; +`; + type WorkflowEditActionAiAgentProps = { action: WorkflowAiAgentAction; actionOptions: @@ -43,8 +56,10 @@ export const WorkflowEditActionAiAgent = ({ defaultTitle: 'AI Agent', }); + const agentId = action.settings.input.agentId; + const { formValues, handleFieldChange, loading } = useAgentUpdateFormState({ - agentId: action.settings.input.agentId, + agentId, readonly: actionOptions.readonly === true, }); @@ -59,10 +74,32 @@ export const WorkflowEditActionAiAgent = ({ const noModelsAvailable = modelOptions.length === 0; + const activeTabId = useRecoilComponentValueV2( + activeTabIdComponentState, + WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID, + ); + + const { rolesOptions, selectedRole, handleRoleChange } = + useAgentRoleAssignment(agentId); + + const tabs = [ + { + id: WorkflowAiAgentTabId.SETTINGS, + title: t`Settings`, + Icon: IconSettings, + }, + { id: WorkflowAiAgentTabId.TOOLS, title: t`Tools`, Icon: IconTool }, + ]; + return loading ? ( ) : ( <> + { if (actionOptions.readonly === true) { @@ -77,41 +114,55 @@ export const WorkflowEditActionAiAgent = ({ disabled={actionOptions.readonly} /> -
- handleFieldChange('modelId', value)} + disabled={actionOptions.readonly || noModelsAvailable} + emptyOption={{ + label: t`No AI models available`, + value: '', + }} + /> - {noModelsAvailable && ( - - {t`You haven't configured any model provider. Please set up an API Key in your instance's admin panel or as an environment variable.`} - - )} -
- handleFieldChange('prompt', value)} - VariablePicker={WorkflowVariablePicker} - multiline - /> - + {noModelsAvailable && ( + + {t`You haven't configured any model provider. Please set up an API Key in your instance's admin panel or as an environment variable.`} + + )} + + handleFieldChange('prompt', value)} + VariablePicker={WorkflowVariablePicker} + multiline + /> + + + )} + {activeTabId === WorkflowAiAgentTabId.TOOLS && ( +