[AI] agent node prompt tab new design + refactor (#19012)
This commit is contained in:
@@ -11,17 +11,13 @@ import { AIChatEditorFocusEffect } from '@/ai/components/internal/AIChatEditorFo
|
||||
import { AIChatSkeletonLoader } from '@/ai/components/internal/AIChatSkeletonLoader';
|
||||
import { SendMessageButton } from '@/ai/components/internal/SendMessageButton';
|
||||
import { useAIChatEditor } from '@/ai/hooks/useAIChatEditor';
|
||||
import { useAiModelOptions } from '@/ai/hooks/useAiModelOptions';
|
||||
import { useAgentChatModelId } from '@/ai/hooks/useAgentChatModelId';
|
||||
import { useWorkspaceAiModelAvailability } from '@/ai/hooks/useWorkspaceAiModelAvailability';
|
||||
import { agentChatUserSelectedModelState } from '@/ai/states/agentChatUserSelectedModelState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { aiModelsState } from '@/client-config/states/aiModelsState';
|
||||
import { getModelIcon } from '@/settings/admin-panel/ai/utils/getModelIcon';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
|
||||
const StyledInputArea = styled.div<{ isMobile: boolean }>`
|
||||
align-items: flex-end;
|
||||
@@ -110,9 +106,18 @@ const StyledRightButtonsContainer = styled.div`
|
||||
|
||||
export const AIChatEditorSection = () => {
|
||||
const isMobile = useIsMobile();
|
||||
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
||||
const aiModels = useAtomStateValue(aiModelsState);
|
||||
const { enabledModels } = useWorkspaceAiModelAvailability();
|
||||
const { options, pinnedOption } = useAiModelOptions({
|
||||
variant: 'pinned-default',
|
||||
});
|
||||
|
||||
const smartModelOptions: SelectOption<string | null>[] = options;
|
||||
const defaultPinnedOption: SelectOption<string | null> | undefined =
|
||||
pinnedOption
|
||||
? {
|
||||
...pinnedOption,
|
||||
value: null,
|
||||
}
|
||||
: undefined;
|
||||
const setAgentChatUserSelectedModel = useSetAtomState(
|
||||
agentChatUserSelectedModelState,
|
||||
);
|
||||
@@ -120,36 +125,6 @@ export const AIChatEditorSection = () => {
|
||||
|
||||
const { editor, handleSendAndClear } = useAIChatEditor();
|
||||
|
||||
const workspaceSmartModel = aiModels.find(
|
||||
(model) => model.modelId === currentWorkspace?.smartModel,
|
||||
);
|
||||
|
||||
const resolvedDefaultModelId = enabledModels.find(
|
||||
(model) =>
|
||||
model.label === workspaceSmartModel?.label &&
|
||||
model.providerName === workspaceSmartModel?.providerName,
|
||||
)?.modelId;
|
||||
|
||||
const defaultPinnedOption = workspaceSmartModel
|
||||
? {
|
||||
value: null as string | null,
|
||||
label: workspaceSmartModel.label,
|
||||
Icon: getModelIcon(
|
||||
workspaceSmartModel.modelFamily,
|
||||
workspaceSmartModel.providerName,
|
||||
),
|
||||
contextualText: t`default`,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const smartModelOptions = enabledModels
|
||||
.filter((model) => model.modelId !== resolvedDefaultModelId)
|
||||
.map((model) => ({
|
||||
value: model.modelId as string | null,
|
||||
label: model.label,
|
||||
Icon: getModelIcon(model.modelFamily, model.providerName),
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<AIChatEditorFocusEffect editor={editor} />
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import { aiModelsState } from '@/client-config/states/aiModelsState';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconBrandX, IconWorld } from 'twenty-ui/display';
|
||||
import { Checkbox } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledCheckboxContainer = styled.div<{ disabled: boolean }>`
|
||||
align-items: center;
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||
display: flex;
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: space-between;
|
||||
padding-inline: ${themeCssVariables.spacing[2]};
|
||||
transition: background-color
|
||||
calc(${themeCssVariables.animation.duration.normal} * 1s) ease;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ disabled }) =>
|
||||
disabled
|
||||
? 'transparent'
|
||||
: themeCssVariables.background.transparent.light};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCheckboxLabel = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
type ModelConfiguration = {
|
||||
webSearch?: {
|
||||
enabled: boolean;
|
||||
configuration?: Record<string, unknown>;
|
||||
};
|
||||
twitterSearch?: {
|
||||
enabled: boolean;
|
||||
configuration?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
type SettingsAgentModelCapabilitiesProps = {
|
||||
selectedModelId: string;
|
||||
modelConfiguration: ModelConfiguration;
|
||||
onConfigurationChange: (configuration: ModelConfiguration) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SettingsAgentModelCapabilities = ({
|
||||
selectedModelId,
|
||||
modelConfiguration,
|
||||
onConfigurationChange,
|
||||
disabled = false,
|
||||
}: SettingsAgentModelCapabilitiesProps) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const aiModels = useAtomStateValue(aiModelsState);
|
||||
|
||||
const selectedModel = aiModels.find((m) => m.modelId === selectedModelId);
|
||||
const nativeCapabilities = selectedModel?.nativeCapabilities;
|
||||
|
||||
if (!isDefined(nativeCapabilities)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!nativeCapabilities.webSearch && !nativeCapabilities.twitterSearch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleCapabilityToggle = (
|
||||
capability: 'webSearch' | 'twitterSearch',
|
||||
enabled: boolean,
|
||||
) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
onConfigurationChange({
|
||||
...modelConfiguration,
|
||||
[capability]: {
|
||||
enabled,
|
||||
configuration: modelConfiguration[capability]?.configuration || {},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const capabilities = [
|
||||
...(nativeCapabilities.webSearch
|
||||
? [
|
||||
{
|
||||
key: 'webSearch' as const,
|
||||
label: t`Web Search`,
|
||||
Icon: IconWorld,
|
||||
enabled: modelConfiguration.webSearch?.enabled || false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(nativeCapabilities.twitterSearch
|
||||
? [
|
||||
{
|
||||
key: 'twitterSearch' as const,
|
||||
label: t`Twitter/X Search`,
|
||||
Icon: IconBrandX,
|
||||
enabled: modelConfiguration.twitterSearch?.enabled || false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<InputLabel>{t`Enable model-specific features`}</InputLabel>
|
||||
<div>
|
||||
{capabilities.map((capability) => (
|
||||
<StyledCheckboxContainer
|
||||
disabled={disabled}
|
||||
key={capability.key}
|
||||
onClick={() =>
|
||||
handleCapabilityToggle(capability.key, !capability.enabled)
|
||||
}
|
||||
>
|
||||
<StyledCheckboxLabel>
|
||||
<capability.Icon size={theme.icon.size.sm} />
|
||||
<span>{capability.label}</span>
|
||||
</StyledCheckboxLabel>
|
||||
<Checkbox
|
||||
checked={capability.enabled}
|
||||
onChange={(event) => {
|
||||
event.stopPropagation();
|
||||
handleCapabilityToggle(capability.key, event.target.checked);
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</StyledCheckboxContainer>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user