diff --git a/packages/twenty-front/src/modules/ai/components/AIChatCreditsExhaustedMessage.tsx b/packages/twenty-front/src/modules/ai/components/AIChatCreditsExhaustedMessage.tsx
index fa023b328ab2d..c37e771fc2bb3 100644
--- a/packages/twenty-front/src/modules/ai/components/AIChatCreditsExhaustedMessage.tsx
+++ b/packages/twenty-front/src/modules/ai/components/AIChatCreditsExhaustedMessage.tsx
@@ -1,67 +1,28 @@
import { AIChatBanner } from '@/ai/components/AIChatBanner';
-import { useEndSubscriptionTrialPeriod } from '@/settings/billing/hooks/useEndSubscriptionTrialPeriod';
-import { useRedirect } from '@/domain-manager/hooks/useRedirect';
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import { t } from '@lingui/core/macro';
-import { useState } from 'react';
import { SettingsPath } from 'twenty-shared/types';
-import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { IconSparkles } from 'twenty-ui/display';
-import { useQuery } from '@apollo/client/react';
import {
PermissionFlagType,
SubscriptionStatus,
- BillingPortalSessionDocument,
} from '~/generated-metadata/graphql';
+import { useNavigateSettings } from '~/hooks/useNavigateSettings';
export const AIChatCreditsExhaustedMessage = () => {
- const { redirect } = useRedirect();
+ const navigateSettings = useNavigateSettings();
const subscriptionStatus = useSubscriptionStatus();
- const { endTrialPeriod, isLoading: isEndingTrial } =
- useEndSubscriptionTrialPeriod();
- const [isProcessing, setIsProcessing] = useState(false);
const isTrialing = subscriptionStatus === SubscriptionStatus.Trialing;
const { [PermissionFlagType.WORKSPACE]: hasPermissionToManageBilling } =
usePermissionFlagMap();
- const { data: billingPortalData, loading: isBillingPortalLoading } = useQuery(
- BillingPortalSessionDocument,
- {
- variables: {
- returnUrlPath: getSettingsPath(SettingsPath.Billing),
- },
- },
- );
-
- const openBillingPortal = () => {
- if (
- isDefined(billingPortalData) &&
- isDefined(billingPortalData.billingPortalSession.url)
- ) {
- redirect(billingPortalData.billingPortalSession.url);
- }
+ const handleUpgradeClick = () => {
+ navigateSettings(SettingsPath.Billing);
};
- const handleUpgradeClick = async () => {
- if (!isTrialing) {
- openBillingPortal();
- return;
- }
-
- setIsProcessing(true);
- const result = await endTrialPeriod();
- setIsProcessing(false);
-
- if (!result.success) {
- openBillingPortal();
- }
- };
-
- const isLoading = isEndingTrial || isBillingPortalLoading || isProcessing;
-
const message = hasPermissionToManageBilling
? isTrialing
? t`Free trial credits exhausted. Subscribe now to continue using AI features.`
@@ -79,8 +40,6 @@ export const AIChatCreditsExhaustedMessage = () => {
buttonOnClick={
hasPermissionToManageBilling ? handleUpgradeClick : undefined
}
- isButtonDisabled={isLoading}
- isButtonLoading={isLoading}
/>
);
};
diff --git a/packages/twenty-front/src/modules/ai/components/AIChatEditorSection.tsx b/packages/twenty-front/src/modules/ai/components/AIChatEditorSection.tsx
index c04638e3bdef4..04667ca3f057b 100644
--- a/packages/twenty-front/src/modules/ai/components/AIChatEditorSection.tsx
+++ b/packages/twenty-front/src/modules/ai/components/AIChatEditorSection.tsx
@@ -1,6 +1,5 @@
import { styled } from '@linaria/react';
import { EditorContent } from '@tiptap/react';
-import { LightButton } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { AIChatEmptyState } from '@/ai/components/AIChatEmptyState';
@@ -12,10 +11,17 @@ 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 { useAiModelLabel } 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';
const StyledInputArea = styled.div<{ isMobile: boolean }>`
align-items: flex-end;
@@ -102,24 +108,48 @@ const StyledRightButtonsContainer = styled.div`
gap: ${themeCssVariables.spacing[1]};
`;
-const StyledReadOnlyModelButtonContainer = styled.div`
- > * {
- cursor: default;
-
- &:hover,
- &:active {
- background: transparent;
- }
- }
-`;
-
export const AIChatEditorSection = () => {
const isMobile = useIsMobile();
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
- const smartModelLabel = useAiModelLabel(currentWorkspace?.smartModel, false);
+ const aiModels = useAtomStateValue(aiModelsState);
+ const { enabledModels } = useWorkspaceAiModelAvailability();
+ const setAgentChatUserSelectedModel = useSetAtomState(
+ agentChatUserSelectedModelState,
+ );
+ const { selectedModelId } = useAgentChatModelId();
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 (
<>
@@ -139,9 +169,16 @@ export const AIChatEditorSection = () => {
-
-
-
+
diff --git a/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx b/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx
index 1e17d8a8334a5..4e9f61f2ce072 100644
--- a/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx
+++ b/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx
@@ -1,4 +1,4 @@
-import { AIChatStandaloneError } from '@/ai/components/AIChatStandaloneError';
+import { AIChatErrorRenderer } from '@/ai/components/AIChatErrorRenderer';
import { AgentMessageRole } from '@/ai/constants/AgentMessageRole';
import { agentChatErrorState } from '@/ai/states/agentChatErrorState';
import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState';
@@ -7,6 +7,12 @@ import { agentChatMessageIdsComponentSelector } from '@/ai/states/agentChatMessa
import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+import { styled } from '@linaria/react';
+import { themeCssVariables } from 'twenty-ui/theme-constants';
+
+const StyledErrorWrapper = styled.div`
+ padding-top: ${themeCssVariables.spacing[3]};
+`;
export const AIChatErrorUnderMessageList = () => {
const agentChatError = useAtomStateValue(agentChatErrorState);
@@ -31,5 +37,9 @@ export const AIChatErrorUnderMessageList = () => {
return null;
}
- return ;
+ return (
+
+
+
+ );
};
diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatAiSdkStreamEffect.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatAiSdkStreamEffect.tsx
index b728822d6dc21..e0df67e2e551d 100644
--- a/packages/twenty-front/src/modules/ai/components/AgentChatAiSdkStreamEffect.tsx
+++ b/packages/twenty-front/src/modules/ai/components/AgentChatAiSdkStreamEffect.tsx
@@ -6,6 +6,7 @@ import { useEnsureAgentChatThreadIdForSend } from '@/ai/hooks/useEnsureAgentChat
import { agentChatErrorState } from '@/ai/states/agentChatErrorState';
import { agentChatIsLoadingState } from '@/ai/states/agentChatIsLoadingState';
import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState';
+import { normalizeAiSdkError } from '@/ai/utils/normalizeAiSdkError';
import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState';
import { agentChatMessagesLoadingState } from '@/ai/states/agentChatMessagesLoadingState';
import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState';
@@ -119,7 +120,7 @@ export const AgentChatAiSdkStreamEffect = () => {
const setAgentChatError = useSetAtomState(agentChatErrorState);
useEffect(() => {
- setAgentChatError(chatState.error);
+ setAgentChatError(normalizeAiSdkError(chatState.error));
}, [chatState.error, setAgentChatError]);
const setAgentChatIsStreaming = useSetAtomState(agentChatIsStreamingState);
diff --git a/packages/twenty-front/src/modules/ai/constants/DefaultFastModel.ts b/packages/twenty-front/src/modules/ai/constants/DefaultFastModel.ts
deleted file mode 100644
index 84e98b83087b1..0000000000000
--- a/packages/twenty-front/src/modules/ai/constants/DefaultFastModel.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const DEFAULT_FAST_MODEL = 'default-fast-model' as const;
diff --git a/packages/twenty-front/src/modules/ai/constants/DefaultSmartModel.ts b/packages/twenty-front/src/modules/ai/constants/DefaultSmartModel.ts
deleted file mode 100644
index f85d6f971916b..0000000000000
--- a/packages/twenty-front/src/modules/ai/constants/DefaultSmartModel.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const DEFAULT_SMART_MODEL = 'default-smart-model' as const;
diff --git a/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts b/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts
index 9bd1d07701830..393f176a5a4a6 100644
--- a/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts
+++ b/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts
@@ -15,6 +15,7 @@ import {
agentChatDraftsByThreadIdState,
} from '@/ai/states/agentChatDraftsByThreadIdState';
import { agentChatInputState } from '@/ai/states/agentChatInputState';
+import { useAgentChatModelId } from '@/ai/hooks/useAgentChatModelId';
import { REST_API_BASE_URL } from '@/apollo/constant/rest-api-base-url';
import { getTokenPair } from '@/apollo/utils/getTokenPair';
import { renewToken } from '@/auth/services/AuthService';
@@ -40,6 +41,7 @@ export const useAgentChat = (
const setTokenPair = useSetAtomState(tokenPairState);
const setAgentChatUsage = useSetAtomState(agentChatUsageState);
+ const { modelIdForRequest } = useAgentChatModelId();
const { getBrowsingContext } = useGetBrowsingContext();
const setCurrentAIChatThreadTitle = useSetAtomState(
currentAIChatThreadTitleState,
@@ -258,6 +260,9 @@ export const useAgentChat = (
body: {
threadId,
browsingContext,
+ ...(isDefined(modelIdForRequest) && {
+ modelId: modelIdForRequest,
+ }),
},
},
);
@@ -273,6 +278,7 @@ export const useAgentChat = (
agentChatUploadedFiles,
setAgentChatUploadedFiles,
setAgentChatDraftsByThreadId,
+ modelIdForRequest,
]);
useListenToBrowserEvent({
diff --git a/packages/twenty-front/src/modules/ai/hooks/useAgentChatModelId.ts b/packages/twenty-front/src/modules/ai/hooks/useAgentChatModelId.ts
new file mode 100644
index 0000000000000..661965d63cf66
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/hooks/useAgentChatModelId.ts
@@ -0,0 +1,23 @@
+import { isDefined } from 'twenty-shared/utils';
+
+import { useWorkspaceAiModelAvailability } from '@/ai/hooks/useWorkspaceAiModelAvailability';
+import { agentChatUserSelectedModelState } from '@/ai/states/agentChatUserSelectedModelState';
+import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+
+export const useAgentChatModelId = () => {
+ const { enabledModels } = useWorkspaceAiModelAvailability();
+ const agentChatUserSelectedModel = useAtomStateValue(
+ agentChatUserSelectedModelState,
+ );
+
+ const isUserModelAvailable =
+ !isDefined(agentChatUserSelectedModel) ||
+ enabledModels.some((model) => model.modelId === agentChatUserSelectedModel);
+
+ const selectedModelId = isUserModelAvailable
+ ? agentChatUserSelectedModel
+ : null;
+ const modelIdForRequest = selectedModelId ?? undefined;
+
+ return { selectedModelId, modelIdForRequest };
+};
diff --git a/packages/twenty-front/src/modules/ai/hooks/useAiModelOptions.ts b/packages/twenty-front/src/modules/ai/hooks/useAiModelOptions.ts
index dd902fba3decc..27398e3543b64 100644
--- a/packages/twenty-front/src/modules/ai/hooks/useAiModelOptions.ts
+++ b/packages/twenty-front/src/modules/ai/hooks/useAiModelOptions.ts
@@ -1,7 +1,6 @@
import { type SelectOption } from 'twenty-ui/input';
+import { isAutoSelectModelId } from 'twenty-shared/utils';
-import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
-import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
import { useWorkspaceAiModelAvailability } from '@/ai/hooks/useWorkspaceAiModelAvailability';
import { aiModelsState } from '@/client-config/states/aiModelsState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
@@ -16,13 +15,11 @@ export const useAiModelOptions = (): SelectOption[] => {
)
.map((model) => ({
value: model.modelId,
- label:
- model.modelId === DEFAULT_FAST_MODEL ||
- model.modelId === DEFAULT_SMART_MODEL
- ? model.label
- : model.modelFamilyLabel
- ? `${model.label} (${model.modelFamilyLabel})`
- : model.label,
+ label: isAutoSelectModelId(model.modelId)
+ ? model.label
+ : model.modelFamilyLabel
+ ? `${model.label} (${model.modelFamilyLabel})`
+ : model.label,
}))
.sort((a, b) => a.label.localeCompare(b.label));
};
@@ -43,11 +40,7 @@ export const useAiModelLabel = (
return modelId;
}
- if (
- model.modelId === DEFAULT_FAST_MODEL ||
- model.modelId === DEFAULT_SMART_MODEL ||
- !includeProvider
- ) {
+ if (isAutoSelectModelId(model.modelId) || !includeProvider) {
return model.label;
}
diff --git a/packages/twenty-front/src/modules/ai/hooks/useWorkspaceAiModelAvailability.ts b/packages/twenty-front/src/modules/ai/hooks/useWorkspaceAiModelAvailability.ts
index 4680a1567ce98..7e844289d8afa 100644
--- a/packages/twenty-front/src/modules/ai/hooks/useWorkspaceAiModelAvailability.ts
+++ b/packages/twenty-front/src/modules/ai/hooks/useWorkspaceAiModelAvailability.ts
@@ -1,17 +1,10 @@
-import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
-import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
+import { isAutoSelectModelId } from 'twenty-shared/utils';
+
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { aiModelsState } from '@/client-config/states/aiModelsState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { type ClientAiModelConfig } from '~/generated-metadata/graphql';
-const VIRTUAL_MODEL_IDS: Set = new Set([
- DEFAULT_SMART_MODEL,
- DEFAULT_FAST_MODEL,
-]);
-
-const isVirtualModel = (modelId: string) => VIRTUAL_MODEL_IDS.has(modelId);
-
export const useWorkspaceAiModelAvailability = () => {
const aiModels = useAtomStateValue(aiModelsState);
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
@@ -23,7 +16,7 @@ export const useWorkspaceAiModelAvailability = () => {
modelId: string,
model?: ClientAiModelConfig,
): boolean => {
- if (isVirtualModel(modelId)) {
+ if (isAutoSelectModelId(modelId)) {
return true;
}
@@ -35,7 +28,7 @@ export const useWorkspaceAiModelAvailability = () => {
};
const realModels = aiModels.filter(
- (model) => !isVirtualModel(model.modelId) && !model.isDeprecated,
+ (model) => !isAutoSelectModelId(model.modelId) && !model.isDeprecated,
);
const enabledModels = realModels.filter((model) =>
diff --git a/packages/twenty-front/src/modules/ai/states/agentChatUserSelectedModelState.ts b/packages/twenty-front/src/modules/ai/states/agentChatUserSelectedModelState.ts
new file mode 100644
index 0000000000000..0383c09b14ec8
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/states/agentChatUserSelectedModelState.ts
@@ -0,0 +1,8 @@
+import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
+
+export const agentChatUserSelectedModelState = createAtomState({
+ key: 'ai/agentChatUserSelectedModel',
+ defaultValue: null,
+ useLocalStorage: true,
+ localStorageOptions: { getOnInit: true },
+});
diff --git a/packages/twenty-front/src/modules/ai/utils/__tests__/normalizeAiSdkError.test.ts b/packages/twenty-front/src/modules/ai/utils/__tests__/normalizeAiSdkError.test.ts
new file mode 100644
index 0000000000000..50088384fc18e
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/utils/__tests__/normalizeAiSdkError.test.ts
@@ -0,0 +1,60 @@
+import { normalizeAiSdkError } from '@/ai/utils/normalizeAiSdkError';
+
+describe('normalizeAiSdkError', () => {
+ it('should return undefined for undefined input', () => {
+ expect(normalizeAiSdkError(undefined)).toBeUndefined();
+ });
+
+ it('should return the error unchanged if it already has a code', () => {
+ const error = new Error('test') as Error & { code: string };
+ error.code = 'EXISTING_CODE';
+
+ const result = normalizeAiSdkError(error);
+
+ expect(result).toBe(error);
+ expect((result as Error & { code: string }).code).toBe('EXISTING_CODE');
+ });
+
+ it('should parse JSON message and attach code from response body', () => {
+ const error = new Error(
+ '{"statusCode":402,"error":"Error","messages":["Credits exhausted"],"code":"BILLING_CREDITS_EXHAUSTED"}',
+ );
+
+ const result = normalizeAiSdkError(error);
+
+ expect(result).not.toBe(error);
+ expect((result as Error & { code: string }).code).toBe(
+ 'BILLING_CREDITS_EXHAUSTED',
+ );
+ expect(result?.message).toBe(error.message);
+ });
+
+ it('should return original error for non-JSON message', () => {
+ const error = new Error('Something went wrong');
+
+ const result = normalizeAiSdkError(error);
+
+ expect(result).toBe(error);
+ });
+
+ it('should return original error for JSON message without code', () => {
+ const error = new Error(
+ '{"statusCode":500,"error":"Internal Server Error"}',
+ );
+
+ const result = normalizeAiSdkError(error);
+
+ expect(result).toBe(error);
+ });
+
+ it('should preserve the original stack trace', () => {
+ const error = new Error(
+ '{"statusCode":402,"code":"BILLING_CREDITS_EXHAUSTED"}',
+ );
+ const originalStack = error.stack;
+
+ const result = normalizeAiSdkError(error);
+
+ expect(result?.stack).toBe(originalStack);
+ });
+});
diff --git a/packages/twenty-front/src/modules/ai/utils/normalizeAiSdkError.ts b/packages/twenty-front/src/modules/ai/utils/normalizeAiSdkError.ts
new file mode 100644
index 0000000000000..bca90d57f1ee7
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/utils/normalizeAiSdkError.ts
@@ -0,0 +1,43 @@
+import { isDefined } from 'twenty-shared/utils';
+
+// The Vercel AI SDK wraps non-200 responses into an Error whose message
+// is the raw JSON response body, losing any custom properties (like `code`).
+// This function parses that JSON and re-attaches the code so downstream
+// consumers (extractErrorCode) can find it without knowing about the SDK.
+export const normalizeAiSdkError = (
+ error: Error | undefined,
+): Error | undefined => {
+ if (!isDefined(error) || !(error instanceof Error)) {
+ return error;
+ }
+
+ if (
+ 'code' in error &&
+ typeof (error as Error & { code: string }).code === 'string'
+ ) {
+ return error;
+ }
+
+ try {
+ const parsed: unknown = JSON.parse(error.message);
+
+ if (
+ isDefined(parsed) &&
+ typeof parsed === 'object' &&
+ 'code' in parsed &&
+ typeof (parsed as { code: unknown }).code === 'string'
+ ) {
+ const normalizedError = new Error(error.message) as Error & {
+ code: string;
+ };
+ normalizedError.code = (parsed as { code: string }).code;
+ normalizedError.stack = error.stack;
+
+ return normalizedError;
+ }
+ } catch {
+ // message is not JSON โ nothing to normalize
+ }
+
+ return error;
+};
diff --git a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts
index 1d7f029277d8f..97f75257e71d8 100644
--- a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts
+++ b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts
@@ -2,8 +2,10 @@ import { gql, InMemoryCache } from '@apollo/client';
import { CombinedGraphQLErrors } from '@apollo/client/errors';
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
-import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
-import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
+import {
+ AUTO_SELECT_FAST_MODEL_ID,
+ AUTO_SELECT_SMART_MODEL_ID,
+} from 'twenty-shared/constants';
import { ApolloFactory, type Options } from '@/apollo/services/apollo.factory';
import { CUSTOM_WORKSPACE_APPLICATION_MOCK } from '@/object-metadata/hooks/__tests__/constants/CustomWorkspaceApplicationMock.test.constant';
import { WorkspaceActivationStatus } from '~/generated-metadata/graphql';
@@ -71,8 +73,8 @@ const mockWorkspace = {
isTwoFactorAuthenticationEnforced: false,
trashRetentionDays: 14,
eventLogRetentionDays: 365 * 3,
- fastModel: DEFAULT_FAST_MODEL,
- smartModel: DEFAULT_SMART_MODEL,
+ fastModel: AUTO_SELECT_FAST_MODEL_ID,
+ smartModel: AUTO_SELECT_SMART_MODEL_ID,
routerModel: 'auto',
enabledAiModelIds: [],
useRecommendedModels: true,
diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromObjectMetadata.test.ts b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromObjectMetadata.test.ts
index e77994d0ef08f..2643bc66ee648 100644
--- a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromObjectMetadata.test.ts
+++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useColumnDefinitionsFromObjectMetadata.test.ts
@@ -1,7 +1,9 @@
import { renderHook } from '@testing-library/react';
-import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
-import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
+import {
+ AUTO_SELECT_FAST_MODEL_ID,
+ AUTO_SELECT_SMART_MODEL_ID,
+} from 'twenty-shared/constants';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { CUSTOM_WORKSPACE_APPLICATION_MOCK } from '@/object-metadata/hooks/__tests__/constants/CustomWorkspaceApplicationMock.test.constant';
import { useColumnDefinitionsFromObjectMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromObjectMetadata';
@@ -68,8 +70,8 @@ describe('useColumnDefinitionsFromObjectMetadata', () => {
isTwoFactorAuthenticationEnforced: false,
trashRetentionDays: 14,
eventLogRetentionDays: 365 * 3,
- fastModel: DEFAULT_FAST_MODEL,
- smartModel: DEFAULT_SMART_MODEL,
+ fastModel: AUTO_SELECT_FAST_MODEL_ID,
+ smartModel: AUTO_SELECT_SMART_MODEL_ID,
enabledAiModelIds: [],
useRecommendedModels: true,
});
diff --git a/packages/twenty-front/src/modules/settings/experience/components/DateTimeSettingsDateFormatSelect.tsx b/packages/twenty-front/src/modules/settings/experience/components/DateTimeSettingsDateFormatSelect.tsx
index fe349367fa3aa..5f2ced128c36f 100644
--- a/packages/twenty-front/src/modules/settings/experience/components/DateTimeSettingsDateFormatSelect.tsx
+++ b/packages/twenty-front/src/modules/settings/experience/components/DateTimeSettingsDateFormatSelect.tsx
@@ -34,38 +34,38 @@ export const DateTimeSettingsDateFormatSelect = ({
return (