AI settings tab (#13496)
https://github.com/user-attachments/assets/87f5a556-ff12-4ce0-aaa7-9120c0432151 --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
+88
-120
@@ -1,32 +1,20 @@
|
||||
import { useAiAgentOutputSchema } from '@/ai/hooks/useAiAgentOutputSchema';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
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';
|
||||
import { AIChatTab } from '@/ai/components/AIChatTab';
|
||||
import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader';
|
||||
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||
import { BaseOutputSchema } from '@/workflow/workflow-variables/types/StepOutputSchema';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
IconMessage,
|
||||
IconSettings,
|
||||
IconTool,
|
||||
useIcons,
|
||||
} from 'twenty-ui/display';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
import { useFindManyAgentsQuery } from '~/generated-metadata/graphql';
|
||||
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
|
||||
import {
|
||||
WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID,
|
||||
WorkflowAiAgentTabId,
|
||||
} from '../constants/workflow-ai-agent-tabs';
|
||||
import { useAgentRoleAssignment } from '@/ai/hooks/useAgentRoleAssignment';
|
||||
import { useAgentUpdateFormState } from '@/ai/hooks/useAgentUpdateFormState';
|
||||
import { useAiAgentOutputSchema } from '@/ai/hooks/useAiAgentOutputSchema';
|
||||
import { useAiModelOptions } from '@/ai/hooks/useAiModelOptions';
|
||||
import { WorkflowOutputSchemaBuilder } from './WorkflowOutputSchemaBuilder';
|
||||
|
||||
const StyledErrorMessage = styled.div`
|
||||
@@ -36,11 +24,6 @@ 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:
|
||||
@@ -55,6 +38,7 @@ export const WorkflowEditActionAiAgent = ({
|
||||
action,
|
||||
actionOptions,
|
||||
}: WorkflowEditActionAiAgentProps) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const { getIcon } = useIcons();
|
||||
const { headerTitle, headerIcon, headerIconColor, headerType } =
|
||||
useWorkflowActionHeader({
|
||||
@@ -62,13 +46,6 @@ export const WorkflowEditActionAiAgent = ({
|
||||
defaultTitle: 'AI Agent',
|
||||
});
|
||||
|
||||
const agentId = action.settings.input.agentId;
|
||||
|
||||
const { formValues, handleFieldChange, loading } = useAgentUpdateFormState({
|
||||
agentId,
|
||||
readonly: actionOptions.readonly === true,
|
||||
});
|
||||
|
||||
const { handleOutputSchemaChange, outputFields } = useAiAgentOutputSchema(
|
||||
action.settings.outputSchema as BaseOutputSchema,
|
||||
actionOptions.readonly === true ? undefined : actionOptions.onActionUpdate,
|
||||
@@ -76,107 +53,98 @@ export const WorkflowEditActionAiAgent = ({
|
||||
actionOptions.readonly,
|
||||
);
|
||||
|
||||
const modelOptions = useAiModelOptions();
|
||||
const { data: agentsData, loading: agentsLoading } = useFindManyAgentsQuery();
|
||||
|
||||
const noModelsAvailable = modelOptions.length === 0;
|
||||
|
||||
const activeTabId = useRecoilComponentValueV2(
|
||||
activeTabIdComponentState,
|
||||
WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID,
|
||||
const agentOptions = (agentsData?.findManyAgents || []).reduce<
|
||||
SelectOption<string>[]
|
||||
>(
|
||||
(acc, agent) => {
|
||||
if (agent.id !== currentWorkspace?.defaultAgent?.id) {
|
||||
acc.push({
|
||||
label: agent.label,
|
||||
value: agent.id,
|
||||
Icon: agent.icon ? getIcon(agent.icon) : undefined,
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[
|
||||
{
|
||||
label: t`No Agent`,
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const { rolesOptions, selectedRole, handleRoleChange } =
|
||||
useAgentRoleAssignment(agentId);
|
||||
const noAgentsAvailable = agentOptions.length === 0;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: WorkflowAiAgentTabId.SETTINGS,
|
||||
title: t`Settings`,
|
||||
Icon: IconSettings,
|
||||
},
|
||||
{ id: WorkflowAiAgentTabId.TOOLS, title: t`Tools`, Icon: IconTool },
|
||||
{ id: WorkflowAiAgentTabId.CHAT, title: t`Chat`, Icon: IconMessage },
|
||||
];
|
||||
const handleFieldChange = (field: 'agentId' | 'prompt', value: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
actionOptions.onActionUpdate?.({
|
||||
...action,
|
||||
settings: {
|
||||
...action.settings,
|
||||
input: {
|
||||
...action.settings.input,
|
||||
[field]: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return loading ? (
|
||||
return agentsLoading ? (
|
||||
<RightDrawerSkeletonLoader />
|
||||
) : (
|
||||
<>
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID}
|
||||
<WorkflowStepHeader
|
||||
onTitleChange={(newName: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
actionOptions.onActionUpdate?.({ ...action, name: newName });
|
||||
}}
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={headerIconColor}
|
||||
initialTitle={headerTitle}
|
||||
headerType={headerType}
|
||||
disabled={actionOptions.readonly}
|
||||
/>
|
||||
{activeTabId === WorkflowAiAgentTabId.CHAT ? (
|
||||
<AIChatTab agentId={agentId} isWorkflowAgentNodeChat />
|
||||
) : (
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
onTitleChange={(newName: string) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
actionOptions.onActionUpdate?.({ ...action, name: newName });
|
||||
}}
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={headerIconColor}
|
||||
initialTitle={headerTitle}
|
||||
headerType={headerType}
|
||||
disabled={actionOptions.readonly}
|
||||
<WorkflowStepBody>
|
||||
<div>
|
||||
<Select
|
||||
dropdownId="select-agent"
|
||||
label={t`Select Agent`}
|
||||
options={agentOptions}
|
||||
value={action.settings.input.agentId || ''}
|
||||
onChange={(value) => handleFieldChange('agentId', value)}
|
||||
disabled={actionOptions.readonly || noAgentsAvailable}
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
{activeTabId === WorkflowAiAgentTabId.SETTINGS && (
|
||||
<>
|
||||
<div>
|
||||
<Select
|
||||
dropdownId="select-model"
|
||||
label={t`AI Model`}
|
||||
options={modelOptions}
|
||||
value={formValues.modelId}
|
||||
onChange={(value) => handleFieldChange('modelId', value)}
|
||||
disabled={actionOptions.readonly || noModelsAvailable}
|
||||
emptyOption={{
|
||||
label: t`Auto`,
|
||||
value: '',
|
||||
}}
|
||||
/>
|
||||
|
||||
{noModelsAvailable && (
|
||||
<StyledErrorMessage>
|
||||
{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.`}
|
||||
</StyledErrorMessage>
|
||||
)}
|
||||
</div>
|
||||
<FormTextFieldInput
|
||||
key={`prompt-${formValues.modelId ? action.id : 'empty'}`}
|
||||
label={t`Instructions for AI`}
|
||||
placeholder={t`Describe what you want the AI to do...`}
|
||||
readonly={actionOptions.readonly}
|
||||
defaultValue={formValues.prompt}
|
||||
onChange={(value) => handleFieldChange('prompt', value)}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
multiline
|
||||
/>
|
||||
<WorkflowOutputSchemaBuilder
|
||||
fields={outputFields}
|
||||
onChange={handleOutputSchemaChange}
|
||||
readonly={actionOptions.readonly}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{activeTabId === WorkflowAiAgentTabId.TOOLS && (
|
||||
<Select
|
||||
dropdownId="select-agent-role"
|
||||
label={t`Assign Role`}
|
||||
options={[{ label: t`No role`, value: '' }, ...rolesOptions]}
|
||||
value={selectedRole || ''}
|
||||
onChange={handleRoleChange}
|
||||
disabled={actionOptions.readonly}
|
||||
/>
|
||||
)}
|
||||
</WorkflowStepBody>
|
||||
</>
|
||||
)}
|
||||
{noAgentsAvailable && (
|
||||
<StyledErrorMessage>
|
||||
{t`Please create agents in the AI settings to use in workflows.`}
|
||||
</StyledErrorMessage>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FormTextFieldInput
|
||||
multiline
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
label={t`Instructions for AI`}
|
||||
placeholder={t`Describe what you want the AI to do...`}
|
||||
defaultValue={action.settings.input.prompt}
|
||||
onChange={(value) => handleFieldChange('prompt', value)}
|
||||
readonly={actionOptions.readonly}
|
||||
/>
|
||||
|
||||
<WorkflowOutputSchemaBuilder
|
||||
fields={outputFields}
|
||||
onChange={handleOutputSchemaChange}
|
||||
readonly={actionOptions.readonly}
|
||||
/>
|
||||
</WorkflowStepBody>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
export enum WorkflowAiAgentTabId {
|
||||
SETTINGS = 'settings',
|
||||
TOOLS = 'tools',
|
||||
CHAT = 'chat',
|
||||
}
|
||||
|
||||
export const WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID =
|
||||
'WORKFLOW_AI_AGENT_TAB_LIST_COMPONENT_ID';
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const workflowAiAgentSelectedRoleState = atom<string | undefined>({
|
||||
key: 'workflowAiAgentSelectedRoleState',
|
||||
default: undefined,
|
||||
});
|
||||
Reference in New Issue
Block a user