diff --git a/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx b/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx index 47e62e9a70..19aefdd8d5 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx @@ -4,12 +4,13 @@ import { isDefined } from 'twenty-shared/utils'; import { AIChatSuggestedPrompts } from '@/ai/components/suggested-prompts/AIChatSuggestedPrompts'; import { AGENT_CHAT_NEW_THREAD_DRAFT_KEY } from '@/ai/states/agentChatDraftsByThreadIdState'; +import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; +import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; import { agentChatMessagesLoadingState } from '@/ai/states/agentChatMessagesLoadingState'; import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState'; -import { agentChatErrorState } from '@/ai/states/agentChatErrorState'; -import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; import { skipMessagesSkeletonUntilLoadedState } from '@/ai/states/skipMessagesSkeletonUntilLoadedState'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; @@ -26,7 +27,11 @@ type AIChatEmptyStateProps = { }; export const AIChatEmptyState = ({ editor }: AIChatEmptyStateProps) => { - const agentChatError = useAtomStateValue(agentChatErrorState); + const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); + const agentChatError = useAtomComponentFamilyStateValue( + agentChatErrorComponentFamilyState, + { threadId: currentAIChatThread }, + ); const agentChatThreadsLoading = useAtomStateValue( agentChatThreadsLoadingState, ); @@ -36,7 +41,6 @@ export const AIChatEmptyState = ({ editor }: AIChatEmptyStateProps) => { const skipMessagesSkeletonUntilLoaded = useAtomStateValue( skipMessagesSkeletonUntilLoadedState, ); - const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); const hasMessages = useAtomComponentSelectorValue( agentChatHasMessageComponentSelector, diff --git a/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx b/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx index 4e9f61f2ce..e4dc114a71 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatErrorUnderMessageList.tsx @@ -1,10 +1,12 @@ import { AIChatErrorRenderer } from '@/ai/components/AIChatErrorRenderer'; import { AgentMessageRole } from '@/ai/constants/AgentMessageRole'; -import { agentChatErrorState } from '@/ai/states/agentChatErrorState'; -import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState'; +import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; +import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; +import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; import { agentChatMessageComponentFamilySelector } from '@/ai/states/agentChatMessageComponentFamilySelector'; import { agentChatMessageIdsComponentSelector } from '@/ai/states/agentChatMessageIdsComponentSelector'; import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { styled } from '@linaria/react'; @@ -15,8 +17,17 @@ const StyledErrorWrapper = styled.div` `; export const AIChatErrorUnderMessageList = () => { - const agentChatError = useAtomStateValue(agentChatErrorState); - const agentChatIsStreaming = useAtomStateValue(agentChatIsStreamingState); + const agentChatDisplayedThread = useAtomStateValue( + agentChatDisplayedThreadState, + ); + const agentChatError = useAtomComponentFamilyStateValue( + agentChatErrorComponentFamilyState, + { threadId: agentChatDisplayedThread }, + ); + const agentChatIsStreaming = useAtomComponentFamilyStateValue( + agentChatIsStreamingComponentFamilyState, + { threadId: agentChatDisplayedThread }, + ); const agentChatMessageIds = useAtomComponentSelectorValue( agentChatMessageIdsComponentSelector, diff --git a/packages/twenty-front/src/modules/ai/components/AIChatLastMessageWithStreamingState.tsx b/packages/twenty-front/src/modules/ai/components/AIChatLastMessageWithStreamingState.tsx index 073f7798f6..2951522dd3 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatLastMessageWithStreamingState.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatLastMessageWithStreamingState.tsx @@ -1,7 +1,9 @@ import { AIChatMessage } from '@/ai/components/AIChatMessage'; -import { agentChatErrorState } from '@/ai/states/agentChatErrorState'; -import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState'; +import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; +import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; +import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; import { agentChatLastMessageIdComponentSelector } from '@/ai/states/agentChatLastMessageIdComponentSelector'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { isDefined } from 'twenty-shared/utils'; @@ -11,8 +13,17 @@ export const AIChatLastMessageWithStreamingState = () => { agentChatLastMessageIdComponentSelector, ); - const agentChatIsStreaming = useAtomStateValue(agentChatIsStreamingState); - const agentChatError = useAtomStateValue(agentChatErrorState); + const agentChatDisplayedThread = useAtomStateValue( + agentChatDisplayedThreadState, + ); + const agentChatIsStreaming = useAtomComponentFamilyStateValue( + agentChatIsStreamingComponentFamilyState, + { threadId: agentChatDisplayedThread }, + ); + const agentChatError = useAtomComponentFamilyStateValue( + agentChatErrorComponentFamilyState, + { threadId: agentChatDisplayedThread }, + ); if (!isDefined(lastMessageId)) { return null; diff --git a/packages/twenty-front/src/modules/ai/components/AIChatStandaloneError.tsx b/packages/twenty-front/src/modules/ai/components/AIChatStandaloneError.tsx index 7d5e8d685f..c276a3d8c0 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatStandaloneError.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatStandaloneError.tsx @@ -1,9 +1,11 @@ import { styled } from '@linaria/react'; import { AIChatErrorRenderer } from '@/ai/components/AIChatErrorRenderer'; -import { agentChatErrorState } from '@/ai/states/agentChatErrorState'; +import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; import { agentChatIsLoadingState } from '@/ai/states/agentChatIsLoadingState'; +import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { isDefined } from 'twenty-shared/utils'; @@ -18,7 +20,11 @@ const StyledErrorContainer = styled.div` export const AIChatStandaloneError = () => { const agentChatIsLoading = useAtomStateValue(agentChatIsLoadingState); - const agentChatError = useAtomStateValue(agentChatErrorState); + const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); + const agentChatError = useAtomComponentFamilyStateValue( + agentChatErrorComponentFamilyState, + { threadId: currentAIChatThread }, + ); const hasMessages = useAtomComponentSelectorValue( agentChatHasMessageComponentSelector, diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatMessagesFetchEffect.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatMessagesFetchEffect.tsx index 3100580263..65591bdc04 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatMessagesFetchEffect.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatMessagesFetchEffect.tsx @@ -4,8 +4,8 @@ import { type AgentChatSubscriptionEvent } from 'twenty-shared/ai'; import { isDefined } from 'twenty-shared/utils'; import { AGENT_CHAT_REFETCH_MESSAGES_EVENT_NAME } from '@/ai/constants/AgentChatRefetchMessagesEventName'; -import { agentChatFirstLiveSeqState } from '@/ai/states/agentChatFirstLiveSeqState'; -import { agentChatHandleEventCallbackState } from '@/ai/states/agentChatHandleEventCallbackState'; +import { agentChatFirstLiveSeqComponentFamilyState } from '@/ai/states/agentChatFirstLiveSeqComponentFamilyState'; +import { agentChatHandleEventCallbackComponentFamilyState } from '@/ai/states/agentChatHandleEventCallbackComponentFamilyState'; import { AGENT_CHAT_NEW_THREAD_DRAFT_KEY } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatFetchedMessagesComponentFamilyState } from '@/ai/states/agentChatFetchedMessagesComponentFamilyState'; import { agentChatMessagesLoadingState } from '@/ai/states/agentChatMessagesLoadingState'; @@ -15,6 +15,7 @@ import { skipMessagesSkeletonUntilLoadedState } from '@/ai/states/skipMessagesSk import { mapDBMessagesToUIMessages } from '@/ai/utils/mapDBMessagesToUIMessages'; import { useQueryWithCallbacks } from '@/apollo/hooks/useQueryWithCallbacks'; import { useListenToBrowserEvent } from '@/browser-event/hooks/useListenToBrowserEvent'; +import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomComponentFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentFamilyState'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; @@ -52,6 +53,14 @@ export const AgentChatMessagesFetchEffect = () => { { threadId: currentAIChatThread }, ); + const handleEventCallbackFamilyCallback = + useAtomComponentFamilyStateCallbackState( + agentChatHandleEventCallbackComponentFamilyState, + ); + const firstLiveSeqFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatFirstLiveSeqComponentFamilyState, + ); + const handleFirstLoad = useCallback( (_data: GetChatMessagesQuery) => { setSkipMessagesSkeletonUntilLoaded(false); @@ -75,13 +84,23 @@ export const AgentChatMessagesFetchEffect = () => { return; } - const handleEvent = store.get(agentChatHandleEventCallbackState.atom); + const threadId = store.get(currentAIChatThreadState.atom); + + if (!isDefined(threadId)) { + return; + } + + const familyKey = { threadId }; + + const handleEvent = store.get( + handleEventCallbackFamilyCallback(familyKey), + ); if (!isDefined(handleEvent)) { return; } - const firstLiveSeq = store.get(agentChatFirstLiveSeqState.atom); + const firstLiveSeq = store.get(firstLiveSeqFamilyCallback(familyKey)); for (let index = 0; index < catchup.chunks.length; index++) { const chunkSeq = index + 1; @@ -97,7 +116,13 @@ export const AgentChatMessagesFetchEffect = () => { } as AgentChatSubscriptionEvent); } }, - [setAgentChatFetchedMessages, setAgentChatQueuedMessages, store], + [ + setAgentChatFetchedMessages, + setAgentChatQueuedMessages, + store, + handleEventCallbackFamilyCallback, + firstLiveSeqFamilyCallback, + ], ); const handleLoadingChange = useCallback( diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatStreamSubscriptionEffect.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatStreamSubscriptionEffect.tsx index 8a8a7378e9..dc5f3d8b8c 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatStreamSubscriptionEffect.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatStreamSubscriptionEffect.tsx @@ -11,7 +11,7 @@ import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThr import { agentChatFetchedMessagesComponentFamilyState } from '@/ai/states/agentChatFetchedMessagesComponentFamilyState'; import { agentChatIsInitialScrollPendingOnThreadChangeState } from '@/ai/states/agentChatIsInitialScrollPendingOnThreadChangeState'; import { agentChatIsLoadingState } from '@/ai/states/agentChatIsLoadingState'; -import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState'; +import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatMessagesLoadingState } from '@/ai/states/agentChatMessagesLoadingState'; import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState'; @@ -57,7 +57,10 @@ export const AgentChatStreamSubscriptionEffect = () => { { threadId: currentAIChatThread }, ); - const agentChatIsStreaming = useAtomStateValue(agentChatIsStreamingState); + const agentChatIsStreaming = useAtomComponentFamilyStateValue( + agentChatIsStreamingComponentFamilyState, + { threadId: currentAIChatThread }, + ); const agentChatDisplayedThread = useAtomStateValue( agentChatDisplayedThreadState, diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx index 6a3b8f1c7c..4bbda19c93 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx @@ -9,15 +9,16 @@ import { import { agentChatInputState } from '@/ai/states/agentChatInputState'; import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState'; import { agentChatThreadsSelector } from '@/ai/states/agentChatThreadsSelector'; -import { agentChatUsageState } from '@/ai/states/agentChatUsageState'; +import { agentChatUsageComponentFamilyState } from '@/ai/states/agentChatUsageComponentFamilyState'; import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; -import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState'; +import { currentAIChatThreadTitleComponentFamilyState } from '@/ai/states/currentAIChatThreadTitleComponentFamilyState'; import { hasInitializedAgentChatThreadsState } from '@/ai/states/hasInitializedAgentChatThreadsState'; import { hasTriggeredCreateForDraftState } from '@/ai/states/hasTriggeredCreateForDraftState'; import { useUpdateMetadataStoreDraft } from '@/metadata-store/hooks/useUpdateMetadataStoreDraft'; import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; import { type FlatAgentChatThread } from '@/metadata-store/types/FlatAgentChatThread'; import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag'; +import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState'; 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'; @@ -37,13 +38,15 @@ export const AgentChatThreadInitializationEffect = () => { const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); const setCurrentAIChatThread = useSetAtomState(currentAIChatThreadState); const setAgentChatInput = useSetAtomState(agentChatInputState); - const setAgentChatUsage = useSetAtomState(agentChatUsageState); - const setCurrentAIChatThreadTitle = useSetAtomState( - currentAIChatThreadTitleState, - ); const setAgentChatThreadsLoading = useSetAtomState( agentChatThreadsLoadingState, ); + const threadTitleFamilyCallback = useAtomComponentFamilyStateCallbackState( + currentAIChatThreadTitleComponentFamilyState, + ); + const agentChatUsageFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatUsageComponentFamilyState, + ); const store = useStore(); const agentChatThreads = useAtomStateValue(agentChatThreadsSelector); const storeEntry = useAtomValue( @@ -113,13 +116,20 @@ export const AgentChatThreadInitializationEffect = () => { setCurrentAIChatThread(firstThread.id); setAgentChatInput(draftForThread); - setCurrentAIChatThreadTitle(firstThread.title ?? null); + + const firstThreadFamilyKey = { threadId: firstThread.id }; + + store.set( + threadTitleFamilyCallback(firstThreadFamilyKey), + firstThread.title ?? null, + ); const hasUsageData = (firstThread.conversationSize ?? 0) > 0 && isDefined(firstThread.contextWindowTokens); - setAgentChatUsage( + store.set( + agentChatUsageFamilyCallback(firstThreadFamilyKey), hasUsageData ? { lastMessage: null, @@ -140,8 +150,6 @@ export const AgentChatThreadInitializationEffect = () => { AGENT_CHAT_NEW_THREAD_DRAFT_KEY ] ?? '', ); - setCurrentAIChatThreadTitle(null); - setAgentChatUsage(null); } }, [ agentChatThreads, @@ -152,9 +160,9 @@ export const AgentChatThreadInitializationEffect = () => { storeEntry.status, setCurrentAIChatThread, setAgentChatInput, - setCurrentAIChatThreadTitle, - setAgentChatUsage, store, + threadTitleFamilyCallback, + agentChatUsageFamilyCallback, ]); return null; diff --git a/packages/twenty-front/src/modules/ai/components/internal/AIChatContextUsageButton.tsx b/packages/twenty-front/src/modules/ai/components/internal/AIChatContextUsageButton.tsx index 7edb4d55b6..b1d30e2118 100644 --- a/packages/twenty-front/src/modules/ai/components/internal/AIChatContextUsageButton.tsx +++ b/packages/twenty-front/src/modules/ai/components/internal/AIChatContextUsageButton.tsx @@ -10,11 +10,13 @@ import { themeCssVariables } from 'twenty-ui/theme-constants'; import { ContextUsageProgressRing } from '@/ai/components/internal/ContextUsageProgressRing'; import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; import { - agentChatUsageState, + agentChatUsageComponentFamilyState, type AgentChatLastMessageUsage, -} from '@/ai/states/agentChatUsageState'; +} from '@/ai/states/agentChatUsageComponentFamilyState'; +import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; import { SettingsBillingLabelValueItem } from '@/settings/billing/components/internal/SettingsBillingLabelValueItem'; import { billingState } from '@/client-config/states/billingState'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { formatNumber } from '~/utils/format/formatNumber'; @@ -94,7 +96,11 @@ const getCachedLabel = (lastMessage: AgentChatLastMessageUsage): string => { export const AIChatContextUsageButton = () => { const { t } = useLingui(); const [isHovered, setIsHovered] = useState(false); - const agentChatUsage = useAtomStateValue(agentChatUsageState); + const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); + const agentChatUsage = useAtomComponentFamilyStateValue( + agentChatUsageComponentFamilyState, + { threadId: currentAIChatThread }, + ); const billing = useAtomStateValue(billingState); const isBillingEnabled = billing?.isBillingEnabled ?? false; diff --git a/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx b/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx index 39b4919880..68b13dd63b 100644 --- a/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx +++ b/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx @@ -1,8 +1,10 @@ import { AGENT_CHAT_STOP_EVENT_NAME } from '@/ai/constants/AgentChatStopEventName'; import { agentChatInputIsEmptySelector } from '@/ai/states/agentChatInputIsEmptySelector'; import { agentChatIsLoadingState } from '@/ai/states/agentChatIsLoadingState'; -import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState'; +import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; +import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; import { dispatchBrowserEvent } from '@/browser-event/utils/dispatchBrowserEvent'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { IconArrowUp, IconPlayerStop } from 'twenty-ui/display'; import { RoundedIconButton } from 'twenty-ui/input'; @@ -18,7 +20,11 @@ export const SendMessageButton = ({ onSend }: SendMessageButtonProps) => { const agentChatIsLoading = useAtomStateValue(agentChatIsLoadingState); - const agentChatIsStreaming = useAtomStateValue(agentChatIsStreamingState); + const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); + const agentChatIsStreaming = useAtomComponentFamilyStateValue( + agentChatIsStreamingComponentFamilyState, + { threadId: currentAIChatThread }, + ); const handleStopClick = () => { dispatchBrowserEvent(AGENT_CHAT_STOP_EVENT_NAME); diff --git a/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts b/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts index a654d65b79..a3c08376d7 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts @@ -1,10 +1,11 @@ import { agentChatDraftsByThreadIdState } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatInputState } from '@/ai/states/agentChatInputState'; -import { agentChatUsageState } from '@/ai/states/agentChatUsageState'; +import { agentChatUsageComponentFamilyState } from '@/ai/states/agentChatUsageComponentFamilyState'; import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; -import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState'; +import { currentAIChatThreadTitleComponentFamilyState } from '@/ai/states/currentAIChatThreadTitleComponentFamilyState'; import { threadIdCreatedFromDraftState } from '@/ai/states/threadIdCreatedFromDraftState'; import { useOpenAskAIPageInSidePanel } from '@/side-panel/hooks/useOpenAskAIPageInSidePanel'; +import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { useStore } from 'jotai'; @@ -26,13 +27,15 @@ export const useAIChatThreadClick = ( currentAIChatThreadState, ); const setAgentChatInput = useSetAtomState(agentChatInputState); - const setCurrentAIChatThreadTitle = useSetAtomState( - currentAIChatThreadTitleState, - ); - const setAgentChatUsage = useSetAtomState(agentChatUsageState); const setAgentChatDraftsByThreadId = useSetAtomState( agentChatDraftsByThreadIdState, ); + const threadTitleFamilyCallback = useAtomComponentFamilyStateCallbackState( + currentAIChatThreadTitleComponentFamilyState, + ); + const agentChatUsageFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatUsageComponentFamilyState, + ); const store = useStore(); const { openAskAIPage } = useOpenAskAIPageInSidePanel(); @@ -54,12 +57,18 @@ export const useAIChatThreadClick = ( setAgentChatInput(newDraft); } - setCurrentAIChatThreadTitle(thread.title ?? null); + const clickedFamilyKey = { threadId: thread.id }; + + store.set( + threadTitleFamilyCallback(clickedFamilyKey), + thread.title ?? null, + ); const hasUsageData = (thread.conversationSize ?? 0) > 0 && isDefined(thread.contextWindowTokens); - setAgentChatUsage( + store.set( + agentChatUsageFamilyCallback(clickedFamilyKey), hasUsageData ? { lastMessage: null, diff --git a/packages/twenty-front/src/modules/ai/hooks/useAgentChatSubscription.ts b/packages/twenty-front/src/modules/ai/hooks/useAgentChatSubscription.ts index 13dcca6acb..dbe21f6ba0 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useAgentChatSubscription.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useAgentChatSubscription.ts @@ -10,17 +10,17 @@ import { import { isDefined } from 'twenty-shared/utils'; import { v4 } from 'uuid'; -import { AGENT_CHAT_INSTANCE_ID } from '@/ai/constants/AgentChatInstanceId'; import { AGENT_CHAT_REFETCH_MESSAGES_EVENT_NAME } from '@/ai/constants/AgentChatRefetchMessagesEventName'; import { ON_AGENT_CHAT_EVENT } from '@/ai/graphql/subscriptions/OnAgentChatEvent'; -import { agentChatErrorState } from '@/ai/states/agentChatErrorState'; -import { agentChatFirstLiveSeqState } from '@/ai/states/agentChatFirstLiveSeqState'; -import { agentChatHandleEventCallbackState } from '@/ai/states/agentChatHandleEventCallbackState'; -import { agentChatIsStreamingState } from '@/ai/states/agentChatIsStreamingState'; +import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; +import { agentChatFirstLiveSeqComponentFamilyState } from '@/ai/states/agentChatFirstLiveSeqComponentFamilyState'; +import { agentChatHandleEventCallbackComponentFamilyState } from '@/ai/states/agentChatHandleEventCallbackComponentFamilyState'; +import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; -import { agentChatUsageState } from '@/ai/states/agentChatUsageState'; -import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState'; +import { agentChatUsageComponentFamilyState } from '@/ai/states/agentChatUsageComponentFamilyState'; +import { currentAIChatThreadTitleComponentFamilyState } from '@/ai/states/currentAIChatThreadTitleComponentFamilyState'; import { dispatchBrowserEvent } from '@/browser-event/utils/dispatchBrowserEvent'; +import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { sseClientState } from '@/sse-db-event/states/sseClientState'; @@ -96,18 +96,52 @@ export const useAgentChatSubscription = (threadId: string | null) => { const store = useStore(); const sseClient = useAtomStateValue(sseClientState); + const errorFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatErrorComponentFamilyState, + ); + const isStreamingFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatIsStreamingComponentFamilyState, + ); + const firstLiveSeqFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatFirstLiveSeqComponentFamilyState, + ); + const handleEventCallbackFamilyCallback = + useAtomComponentFamilyStateCallbackState( + agentChatHandleEventCallbackComponentFamilyState, + ); + const messagesFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatMessagesComponentFamilyState, + ); + const usageFamilyCallback = useAtomComponentFamilyStateCallbackState( + agentChatUsageComponentFamilyState, + ); + const threadTitleFamilyCallback = useAtomComponentFamilyStateCallbackState( + currentAIChatThreadTitleComponentFamilyState, + ); + useEffect(() => { if (!isDefined(threadId) || !isDefined(sseClient)) { return; } + const familyKey = { threadId }; + + const errorAtom = errorFamilyCallback(familyKey); + const isStreamingAtom = isStreamingFamilyCallback(familyKey); + const firstLiveSeqAtom = firstLiveSeqFamilyCallback(familyKey); + const handleEventCallbackAtom = + handleEventCallbackFamilyCallback(familyKey); + const messagesAtom = messagesFamilyCallback(familyKey); + const usageAtom = usageFamilyCallback(familyKey); + const threadTitleAtom = threadTitleFamilyCallback(familyKey); + let bridge: TransformStream | null = null; let throttleTimer: ReturnType | null = null; let latestMessage: ExtendedUIMessage | null = null; let writer: WritableStreamDefaultWriter | null = null; let disposed = false; - store.set(agentChatFirstLiveSeqState.atom, null); + store.set(firstLiveSeqAtom, null); const closeWriter = () => { if (isDefined(writer)) { @@ -119,8 +153,8 @@ export const useAgentChatSubscription = (threadId: string | null) => { const cleanupStream = () => { closeWriter(); - if (store.get(agentChatIsStreamingState.atom)) { - store.set(agentChatIsStreamingState.atom, false); + if (store.get(isStreamingAtom)) { + store.set(isStreamingAtom, false); } }; @@ -131,14 +165,7 @@ export const useAgentChatSubscription = (threadId: string | null) => { return; } - const atomKey = { - instanceId: AGENT_CHAT_INSTANCE_ID, - familyKey: { threadId }, - }; - - const currentMessages = store.get( - agentChatMessagesComponentFamilyState.atomFamily(atomKey), - ); + const currentMessages = store.get(messagesAtom); const streamingMsgIndex = currentMessages.findIndex( (message) => message.id === messageToFlush.id, @@ -148,15 +175,9 @@ export const useAgentChatSubscription = (threadId: string | null) => { const updatedMessages = [...currentMessages]; updatedMessages[streamingMsgIndex] = messageToFlush; - store.set( - agentChatMessagesComponentFamilyState.atomFamily(atomKey), - updatedMessages, - ); + store.set(messagesAtom, updatedMessages); } else { - store.set(agentChatMessagesComponentFamilyState.atomFamily(atomKey), [ - ...currentMessages, - messageToFlush, - ]); + store.set(messagesAtom, [...currentMessages, messageToFlush]); } }; @@ -184,7 +205,7 @@ export const useAgentChatSubscription = (threadId: string | null) => { ); if (isDefined(titlePart) && titlePart.type === 'data-thread-title') { - store.set(currentAIChatThreadTitleState.atom, titlePart.data.title); + store.set(threadTitleAtom, titlePart.data.title); } const metadata = extendedMessage.metadata as @@ -207,7 +228,7 @@ export const useAgentChatSubscription = (threadId: string | null) => { const usage = metadata.usage; const model = metadata.model; - store.set(agentChatUsageState.atom, (prev) => ({ + store.set(usageAtom, (prev) => ({ lastMessage: { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens, @@ -234,22 +255,20 @@ export const useAgentChatSubscription = (threadId: string | null) => { flushToAtom(); if (!disposed) { - store.set(agentChatIsStreamingState.atom, false); + store.set(isStreamingAtom, false); } }; const handleEvent = (event: AgentChatSubscriptionEvent) => { switch (event.type) { case 'stream-chunk': { - if ( - isDefined(event.seq) && - store.get(agentChatFirstLiveSeqState.atom) === null - ) { - store.set(agentChatFirstLiveSeqState.atom, event.seq); + if (isDefined(event.seq) && store.get(firstLiveSeqAtom) === null) { + store.set(firstLiveSeqAtom, event.seq); } - if (!store.get(agentChatIsStreamingState.atom)) { - store.set(agentChatIsStreamingState.atom, true); + if (!store.get(isStreamingAtom)) { + store.set(isStreamingAtom, true); + store.set(errorAtom, null); bridge = new TransformStream(); writer = bridge.writable.getWriter(); @@ -260,7 +279,7 @@ export const useAgentChatSubscription = (threadId: string | null) => { startReadLoop(adaptedReadable).catch(() => { if (!disposed) { - store.set(agentChatIsStreamingState.atom, false); + store.set(isStreamingAtom, false); } }); } @@ -288,16 +307,16 @@ export const useAgentChatSubscription = (threadId: string | null) => { }; streamError.code = event.code; - store.set(agentChatErrorState.atom, streamError); + store.set(errorAtom, streamError); closeWriter(); - store.set(agentChatIsStreamingState.atom, false); + store.set(isStreamingAtom, false); break; } } }; - store.set(agentChatHandleEventCallbackState.atom, () => handleEvent); + store.set(handleEventCallbackAtom, () => handleEvent); const dispose = sseClient.subscribe( { @@ -325,12 +344,23 @@ export const useAgentChatSubscription = (threadId: string | null) => { return () => { disposed = true; - store.set(agentChatHandleEventCallbackState.atom, null); + store.set(handleEventCallbackAtom, null); if (isDefined(throttleTimer)) { clearTimeout(throttleTimer); } cleanupStream(); dispose(); }; - }, [threadId, sseClient, store]); + }, [ + threadId, + sseClient, + store, + errorFamilyCallback, + isStreamingFamilyCallback, + firstLiveSeqFamilyCallback, + handleEventCallbackFamilyCallback, + messagesFamilyCallback, + usageFamilyCallback, + threadTitleFamilyCallback, + ]); }; diff --git a/packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts b/packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts index 0ea45500de..5f964c8226 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useCreateAgentChatThread.ts @@ -5,9 +5,7 @@ import { agentChatDraftsByThreadIdState, } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatInputState } from '@/ai/states/agentChatInputState'; -import { agentChatUsageState } from '@/ai/states/agentChatUsageState'; import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; -import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState'; import { shouldFocusChatEditorState } from '@/ai/states/shouldFocusChatEditorState'; import { hasTriggeredCreateForDraftState } from '@/ai/states/hasTriggeredCreateForDraftState'; import { isCreatingChatThreadState } from '@/ai/states/isCreatingChatThreadState'; @@ -24,10 +22,6 @@ import { CreateChatThreadDocument } from '~/generated-metadata/graphql'; export const useCreateAgentChatThread = () => { const setCurrentAIChatThread = useSetAtomState(currentAIChatThreadState); const setAgentChatInput = useSetAtomState(agentChatInputState); - const setAgentChatUsage = useSetAtomState(agentChatUsageState); - const setCurrentAIChatThreadTitle = useSetAtomState( - currentAIChatThreadTitleState, - ); const setIsCreatingChatThread = useSetAtomState(isCreatingChatThreadState); const setAgentChatDraftsByThreadId = useSetAtomState( agentChatDraftsByThreadIdState, @@ -87,8 +81,6 @@ export const useCreateAgentChatThread = () => { setCurrentAIChatThread(newThreadId); setAgentChatInput(newDraft); - setCurrentAIChatThreadTitle(null); - setAgentChatUsage(null); }, onError: () => { setIsCreatingChatThread(false); diff --git a/packages/twenty-front/src/modules/ai/hooks/useSwitchToNewAIChat.ts b/packages/twenty-front/src/modules/ai/hooks/useSwitchToNewAIChat.ts index 5cf182f475..86f53d03c1 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useSwitchToNewAIChat.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useSwitchToNewAIChat.ts @@ -5,9 +5,7 @@ import { agentChatDraftsByThreadIdState, } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatInputState } from '@/ai/states/agentChatInputState'; -import { agentChatUsageState } from '@/ai/states/agentChatUsageState'; import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; -import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState'; import { shouldFocusChatEditorState } from '@/ai/states/shouldFocusChatEditorState'; import { hasTriggeredCreateForDraftState } from '@/ai/states/hasTriggeredCreateForDraftState'; import { threadIdCreatedFromDraftState } from '@/ai/states/threadIdCreatedFromDraftState'; @@ -23,10 +21,6 @@ export const useSwitchToNewAIChat = () => { currentAIChatThreadState, ); const setAgentChatInput = useSetAtomState(agentChatInputState); - const setAgentChatUsage = useSetAtomState(agentChatUsageState); - const setCurrentAIChatThreadTitle = useSetAtomState( - currentAIChatThreadTitleState, - ); const setAgentChatDraftsByThreadId = useSetAtomState( agentChatDraftsByThreadIdState, ); @@ -48,8 +42,6 @@ export const useSwitchToNewAIChat = () => { store.set(hasTriggeredCreateForDraftState.atom, false); setCurrentAIChatThread(AGENT_CHAT_NEW_THREAD_DRAFT_KEY); setAgentChatInput(newChatDraft); - setCurrentAIChatThreadTitle(null); - setAgentChatUsage(null); openAskAIPage(); store.set(shouldFocusChatEditorState.atom, true); }; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts new file mode 100644 index 0000000000..63e6df252c --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts @@ -0,0 +1,9 @@ +import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; + +export const agentChatErrorComponentFamilyState = + createAtomComponentFamilyState({ + key: 'agentChatErrorComponentFamilyState', + defaultValue: null, + componentInstanceContext: AgentChatComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatErrorState.ts b/packages/twenty-front/src/modules/ai/states/agentChatErrorState.ts deleted file mode 100644 index e745ec0afc..0000000000 --- a/packages/twenty-front/src/modules/ai/states/agentChatErrorState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; - -export const agentChatErrorState = createAtomState({ - key: 'agentChatErrorState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts new file mode 100644 index 0000000000..e8a49d4b55 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts @@ -0,0 +1,9 @@ +import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; + +export const agentChatFirstLiveSeqComponentFamilyState = + createAtomComponentFamilyState({ + key: 'agentChatFirstLiveSeqComponentFamilyState', + defaultValue: null, + componentInstanceContext: AgentChatComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqState.ts b/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqState.ts deleted file mode 100644 index c4e99fdf06..0000000000 --- a/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; - -export const agentChatFirstLiveSeqState = createAtomState({ - key: 'agentChatFirstLiveSeqState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts new file mode 100644 index 0000000000..d938e979f3 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts @@ -0,0 +1,14 @@ +import { type AgentChatSubscriptionEvent } from 'twenty-shared/ai'; + +import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; + +export const agentChatHandleEventCallbackComponentFamilyState = + createAtomComponentFamilyState< + ((event: AgentChatSubscriptionEvent) => void) | null, + { threadId: string | null } + >({ + key: 'agentChatHandleEventCallbackComponentFamilyState', + defaultValue: null, + componentInstanceContext: AgentChatComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackState.ts b/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackState.ts deleted file mode 100644 index b1e764b1b4..0000000000 --- a/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { type AgentChatSubscriptionEvent } from 'twenty-shared/ai'; - -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; - -export const agentChatHandleEventCallbackState = createAtomState< - ((event: AgentChatSubscriptionEvent) => void) | null ->({ - key: 'agentChatHandleEventCallbackState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts new file mode 100644 index 0000000000..13db578033 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts @@ -0,0 +1,9 @@ +import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; + +export const agentChatIsStreamingComponentFamilyState = + createAtomComponentFamilyState({ + key: 'agentChatIsStreamingComponentFamilyState', + defaultValue: false, + componentInstanceContext: AgentChatComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingState.ts b/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingState.ts deleted file mode 100644 index 321be705d1..0000000000 --- a/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; - -export const agentChatIsStreamingState = createAtomState({ - key: 'agentChatIsStreamingState', - defaultValue: false, -}); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts new file mode 100644 index 0000000000..691aada9e4 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts @@ -0,0 +1,30 @@ +import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; + +export type AgentChatLastMessageUsage = { + inputTokens: number; + outputTokens: number; + cachedInputTokens: number; + inputCredits: number; + outputCredits: number; +}; + +export type AgentChatUsageState = { + lastMessage: AgentChatLastMessageUsage | null; + conversationSize: number; + contextWindowTokens: number; + inputTokens: number; + outputTokens: number; + inputCredits: number; + outputCredits: number; +}; + +export const agentChatUsageComponentFamilyState = + createAtomComponentFamilyState< + AgentChatUsageState | null, + { threadId: string | null } + >({ + key: 'agentChatUsageComponentFamilyState', + defaultValue: null, + componentInstanceContext: AgentChatComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatUsageState.ts b/packages/twenty-front/src/modules/ai/states/agentChatUsageState.ts deleted file mode 100644 index 5c892c863f..0000000000 --- a/packages/twenty-front/src/modules/ai/states/agentChatUsageState.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; - -export type AgentChatLastMessageUsage = { - inputTokens: number; - outputTokens: number; - cachedInputTokens: number; - inputCredits: number; - outputCredits: number; -}; - -export type AgentChatUsageState = { - lastMessage: AgentChatLastMessageUsage | null; - conversationSize: number; - contextWindowTokens: number; - inputTokens: number; - outputTokens: number; - inputCredits: number; - outputCredits: number; -}; - -export const agentChatUsageState = createAtomState({ - key: 'agentChatUsageState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/ai/states/currentAIChatThreadTitleComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/currentAIChatThreadTitleComponentFamilyState.ts new file mode 100644 index 0000000000..95e125c36b --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/currentAIChatThreadTitleComponentFamilyState.ts @@ -0,0 +1,9 @@ +import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; + +export const currentAIChatThreadTitleComponentFamilyState = + createAtomComponentFamilyState({ + key: 'currentAIChatThreadTitleComponentFamilyState', + defaultValue: null, + componentInstanceContext: AgentChatComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ai/states/currentAIChatThreadTitleState.ts b/packages/twenty-front/src/modules/ai/states/currentAIChatThreadTitleState.ts deleted file mode 100644 index 72a9aac137..0000000000 --- a/packages/twenty-front/src/modules/ai/states/currentAIChatThreadTitleState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; - -export const currentAIChatThreadTitleState = createAtomState({ - key: 'ai/currentAIChatThreadTitleState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/side-panel/components/SidePanelAskAIInfo.tsx b/packages/twenty-front/src/modules/side-panel/components/SidePanelAskAIInfo.tsx index 6707da8f6b..ad3afbdc37 100644 --- a/packages/twenty-front/src/modules/side-panel/components/SidePanelAskAIInfo.tsx +++ b/packages/twenty-front/src/modules/side-panel/components/SidePanelAskAIInfo.tsx @@ -1,4 +1,6 @@ -import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState'; +import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState'; +import { currentAIChatThreadTitleComponentFamilyState } from '@/ai/states/currentAIChatThreadTitleComponentFamilyState'; +import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { styled } from '@linaria/react'; import { t } from '@lingui/core/macro'; @@ -12,8 +14,10 @@ const StyledPageTitle = styled.div` `; export const SidePanelAskAIInfo = () => { - const currentAIChatThreadTitle = useAtomStateValue( - currentAIChatThreadTitleState, + const currentAIChatThread = useAtomStateValue(currentAIChatThreadState); + const currentAIChatThreadTitle = useAtomComponentFamilyStateValue( + currentAIChatThreadTitleComponentFamilyState, + { threadId: currentAIChatThread }, ); return (