Skip to content

Commit c577e38

Browse files
authored
⚛️ refactor: Move the Steer Atoms to Jotai in the Chat Package (#17135)
1 parent 1a0c23a commit c577e38

45 files changed

Lines changed: 1187 additions & 1094 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎client/src/Providers/MessagePartsHostContext.tsx‎

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { useCallback, useMemo } from 'react';
22
import { useAtomValue, useSetAtom } from 'jotai';
33
import { useToastContext } from '@librechat/client';
4-
import { setDefaultMessagePartsHost } from '@librechat/chat/react';
4+
import { setDefaultMessagePartsHost, pendingSteersByConvoId } from '@librechat/chat/react';
5+
import {
6+
liveAppliedSteerFamily,
7+
liveAppliedSteerIdsAtom,
8+
escalatingSteerFamily,
9+
} from '@librechat/chat/react';
510
import {
611
useRecoilCallback,
712
useRecoilState,
@@ -17,11 +22,6 @@ import type {
1722
MessagePartsHost,
1823
} from '~/hooks/Chat/contract';
1924
import type { Artifact } from '~/common';
20-
import {
21-
liveAppliedSteerFamily,
22-
liveAppliedSteerIdsAtom,
23-
escalatingSteerFamily,
24-
} from '~/store/steer';
2525
import store, { ptcTraceByToolCallId, ptcTraceKey, sandboxStartingByToolCallId } from '~/store';
2626
import { useMessageContext, useFileMapContext, useAgentsMapContext } from '~/Providers';
2727
import { getAppPartRenderers } from '~/components/Chat/Messages/Content/Parts/slots';
@@ -124,8 +124,7 @@ export const appMessagePartsHost: MessagePartsHost = {
124124
),
125125
useToolArtifactClaim: (artifactId) => useRecoilState(store.toolArtifactClaim(artifactId)),
126126
useArtifactPanel: useAppArtifactPanel,
127-
usePendingSteers: (conversationId) =>
128-
useRecoilValue(store.pendingSteersByConvoId(conversationId)),
127+
usePendingSteers: (conversationId) => useAtomValue(pendingSteersByConvoId(conversationId)),
129128
useSteerEscalating: (conversationId) => useAtomValue(escalatingSteerFamily(conversationId)),
130129
usePaneConversationId: (index) => useRecoilValue(store.conversationIdByIndex(index)),
131130
useLiveAppliedSteer: useAppLiveAppliedSteer,

‎client/src/components/Chat/Input/Composer/Queue.tsx‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { memo, useId, useRef, useMemo, useState, useCallback } from 'react';
22
import { useAtomValue } from 'jotai';
3-
import { useRecoilValue } from 'recoil';
43
import { createPortal } from 'react-dom';
54
import * as Ariakit from '@ariakit/react';
65
import { useDrag, useDrop } from 'react-dnd';
76
import { AnimatePresence, motion, useReducedMotion } from 'framer-motion';
87
import { claimQueuedIntent, releaseQueuedIntent, hasQueuedIntent } from '@librechat/chat';
8+
import {
9+
escalatingSteerFamily,
10+
revealedQueuedTurnFamily,
11+
pendingSteersByConvoId,
12+
} from '@librechat/chat/react';
913
import {
1014
Button,
1115
IconButton,
@@ -31,12 +35,10 @@ import type { RestoreToComposer } from '~/Providers/ComposerRestoreContext';
3135
import type { SteeringControls } from '~/hooks/Chat/useSteering';
3236
import type { QueuedMessage } from '~/hooks/Chat/queue';
3337
import { useQueuedTurnPortal } from '~/components/Chat/Steering/QueuedTurnPortal';
34-
import { escalatingSteerFamily, revealedQueuedTurnFamily } from '~/store/steer';
3538
import EscalateNowButton from '~/components/Chat/Input/EscalateNowButton';
3639
import { queuedMessagesByConvoId } from '~/hooks/Chat/queue';
3740
import { useLocalize } from '~/hooks';
3841
import { cn } from '~/utils';
39-
import store from '~/store';
4042

4143
const DRAG_TYPE = 'queued-message';
4244

@@ -733,7 +735,7 @@ function Queue({
733735
`aria-describedby` at whichever copy the document happened to keep. */
734736
const reorderHintId = useId();
735737
const queued = useAtomValue(queuedMessagesByConvoId(steering.queueKey));
736-
const pendingSteers = useRecoilValue(store.pendingSteersByConvoId(conversationId));
738+
const pendingSteers = useAtomValue(pendingSteersByConvoId(conversationId));
737739
const escalating = useAtomValue(escalatingSteerFamily(conversationId));
738740
/* Only one interrupt can be unresolved at a time: a second arm would seal the
739741
same run twice. The escalating flag covers an arm's round trip, before its

‎client/src/components/Chat/Input/Composer/__tests__/Queue.spec.tsx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getDefaultStore, useSetAtom } from 'jotai';
55
import { HTML5Backend } from 'react-dnd-html5-backend';
66
import { ReasoningEffort } from 'librechat-data-provider';
77
import { hasQueuedIntent, releaseQueuedIntent } from '@librechat/chat';
8+
import { revealedQueuedTurnFamily, resetSteerFamilies } from '@librechat/chat/react';
89
import { act, render, screen, within, waitFor, fireEvent } from '@testing-library/react';
910
import type { SteeringControls } from '~/hooks/Chat/useSteering';
1011
import type { QueuedMessage } from '~/hooks/Chat/queue';
@@ -13,7 +14,6 @@ import {
1314
useQueuedTurnPortal,
1415
} from '~/components/Chat/Steering/QueuedTurnPortal';
1516
import { queuedMessagesByConvoId, resetQueueFamilies } from '~/hooks/Chat/queue';
16-
import { revealedQueuedTurnFamily } from '~/store/steer';
1717
import Queue from '../Queue';
1818
jest.mock('~/hooks', () => ({
1919
useLocalize: () => (key: string, options?: Record<string, string | number>) => {
@@ -201,7 +201,10 @@ function renderQueue(
201201
);
202202
}
203203

204-
beforeEach(() => resetQueueFamilies());
204+
beforeEach(() => {
205+
resetQueueFamilies();
206+
resetSteerFamilies();
207+
});
205208

206209
describe('Queue', () => {
207210
beforeEach(() => jest.clearAllMocks());

‎client/src/components/Chat/Input/__tests__/ChatForm.queryParams.spec.tsx‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { QueryKeys, EModelEndpoint } from 'librechat-data-provider';
88
import { act, render, screen, fireEvent } from '@testing-library/react';
99
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
1010
import { MemoryRouter, useNavigate, useLocation } from 'react-router-dom';
11+
import {
12+
activeGenerationCreatedAtByConvoId,
13+
activeGenerationProtocolVersionByConvoId,
14+
} from '@librechat/chat/react';
1115
import type { TConversation } from 'librechat-data-provider';
1216
import type { Transport } from '~/hooks/Chat/contract';
1317
import type { ChatFormValues } from '~/common';
@@ -167,6 +171,8 @@ function mountComposer(
167171
const jotaiStore = createStore();
168172
if (liveRun) {
169173
jotaiStore.set(showStopButtonByIndex(0), true);
174+
jotaiStore.set(activeGenerationCreatedAtByConvoId(conversation.conversationId ?? ''), 1000);
175+
jotaiStore.set(activeGenerationProtocolVersionByConvoId(conversation.conversationId ?? ''), 2);
170176
}
171177
const wrapper = ({ children }: { children: React.ReactNode }) => (
172178
<QueryClientProvider client={queryClient}>
@@ -175,14 +181,6 @@ function mountComposer(
175181
initializeState={({ set }) => {
176182
if (liveRun) {
177183
set(store.isSubmittingFamily(0), true);
178-
set(
179-
store.activeGenerationCreatedAtByConvoId(conversation.conversationId ?? ''),
180-
1000,
181-
);
182-
set(
183-
store.activeGenerationProtocolVersionByConvoId(conversation.conversationId ?? ''),
184-
2,
185-
);
186184
}
187185
if (speechSettingsInitialized) {
188186
set(store.engineSTT, 'external');

‎client/src/components/Chat/Input/__tests__/QueuedTurnPortal.spec.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { RecoilRoot } from 'recoil';
33
import { EModelEndpoint } from 'librechat-data-provider';
44
import { act, render, screen } from '@testing-library/react';
55
import { Provider as JotaiProvider, createStore } from 'jotai';
6+
import { revealedQueuedTurnFamily } from '@librechat/chat/react';
67
import type { TConversation } from 'librechat-data-provider';
78
import {
89
QueuedTurnPortalProvider,
910
useQueuedTurnPortal,
1011
} from '~/components/Chat/Steering/QueuedTurnPortal';
1112
import PendingTurn from '~/components/Chat/Messages/PendingTurn';
12-
import { revealedQueuedTurnFamily } from '~/store/steer';
1313
import { ChatContext } from '~/Providers';
1414

1515
jest.mock('~/hooks', () => ({ useLocalize: () => (key: string) => key }));

‎client/src/components/Chat/Messages/Content/ApprovalContext.tsx‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {
77
useRef,
88
useState,
99
} from 'react';
10-
import { useRecoilValue } from 'recoil';
11-
import { useAtom, useStore } from 'jotai';
1210
import { Constants } from 'librechat-data-provider';
11+
import { useAtom, useStore, useAtomValue } from 'jotai';
12+
import { activeGenerationCreatedAtByConvoId } from '@librechat/chat/react';
1313
import type { Agents } from 'librechat-data-provider';
1414
import type { AskAnswerStatus } from '~/components/Chat/ask/state';
1515
import {
@@ -22,7 +22,6 @@ import { askSubmitStatusAtom } from '~/components/Chat/ask/state';
2222
import { resolveAskUserQuestionPart } from '~/utils/approval';
2323
import { ChatContext } from '~/Providers/ChatContext';
2424
import { useGetEphemeralAgent } from '~/store/agents';
25-
import store from '~/store';
2625

2726
/** Shared read/write for the ask-answer submit status. */
2827
export function useAskSubmitStatus(): {
@@ -438,8 +437,8 @@ export function useResumeSubmit() {
438437
/** Ask status lives in Jotai so it works from the composer (outside the
439438
* provider); tool-approval status stays on the context. */
440439
const { setAskStatus } = useAskSubmitStatus();
441-
const activeGenerationCreatedAt = useRecoilValue(
442-
store.activeGenerationCreatedAtByConvoId(conversation?.conversationId ?? Constants.NEW_CONVO),
440+
const activeGenerationCreatedAt = useAtomValue(
441+
activeGenerationCreatedAtByConvoId(conversation?.conversationId ?? Constants.NEW_CONVO),
443442
);
444443

445444
const buildResumeFields = useCallback((): ResumeAgentFields | null => {

‎client/src/components/Chat/Messages/Content/Parts/__tests__/PendingSteers.test.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import { RecoilRoot } from 'recoil';
33
import { MemoryRouter } from 'react-router-dom';
44
import { Provider as JotaiProvider, createStore } from 'jotai';
5+
import { pendingSteersByConvoId } from '@librechat/chat/react';
56
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
67
import { Constants, ContentTypes, QueryKeys } from 'librechat-data-provider';
78
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
@@ -86,6 +87,7 @@ function renderPending(
8687
});
8788
queryClient.setQueryData([QueryKeys.messages, CONVO_ID], messages ?? []);
8889
const jotaiStore = createStore();
90+
jotaiStore.set(pendingSteersByConvoId(CONVO_ID), steers);
8991
if (activeSiblingIndex != null) {
9092
jotaiStore.set(
9193
siblingIdxFamily(
@@ -110,7 +112,6 @@ function renderPending(
110112
conversationId: CONVO_ID,
111113
} as TConversation);
112114
}
113-
set(store.pendingSteersByConvoId(CONVO_ID), steers);
114115
}}
115116
>
116117
<PendingSteers conversationId={CONVO_ID} index={pane?.index} fullWidth={fullWidth} />

‎client/src/components/Chat/Messages/Content/Parts/__tests__/SteerPart.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import React from 'react';
22
import { RecoilRoot } from 'recoil';
33
import { QueryKeys } from 'librechat-data-provider';
44
import { Provider, createStore, useAtomValue } from 'jotai';
5+
import { liveAppliedSteerIdsAtom } from '@librechat/chat/react';
56
import { render, screen, fireEvent } from '@testing-library/react';
67
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
78
import type { TMessage } from 'librechat-data-provider';
89
import { STEER_ICON } from '~/components/Chat/Steering/identity';
9-
import { liveAppliedSteerIdsAtom } from '~/store/steer';
1010
import SteerPart from '../SteerPart';
1111
import store from '~/store';
1212

‎client/src/components/Chat/Messages/Content/__tests__/ApprovalContext.test.tsx‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import React from 'react';
22
import { RecoilRoot } from 'recoil';
33
import { act, renderHook } from '@testing-library/react';
44
import { Provider as JotaiProvider, createStore } from 'jotai';
5-
import type { MutableSnapshot } from 'recoil';
5+
import { activeGenerationCreatedAtByConvoId } from '@librechat/chat/react';
66
import ApprovalProvider, { useApprovalContext, useResumeSubmit } from '../ApprovalContext';
77
import { ChatContext } from '~/Providers/ChatContext';
8-
import store from '~/store';
98

109
const mockApprovalMutate = jest.fn();
1110
const mockAskMutate = jest.fn();
@@ -29,17 +28,15 @@ const chatContextValue = {
2928

3029
const createWrapper = (generationCreatedAt: number | null) =>
3130
function ResumeWrapper({ children }: { children: React.ReactNode }) {
32-
const jotaiStore = React.useRef(createStore()).current;
33-
const initializeState = (snapshot: MutableSnapshot) => {
31+
const [jotaiStore] = React.useState(() => {
32+
const created = createStore();
3433
if (generationCreatedAt != null) {
35-
snapshot.set(
36-
store.activeGenerationCreatedAtByConvoId('conversation-1'),
37-
generationCreatedAt,
38-
);
34+
created.set(activeGenerationCreatedAtByConvoId('conversation-1'), generationCreatedAt);
3935
}
40-
};
36+
return created;
37+
});
4138
return (
42-
<RecoilRoot initializeState={initializeState}>
39+
<RecoilRoot>
4340
<JotaiProvider store={jotaiStore}>
4441
<ChatContext.Provider value={chatContextValue}>
4542
<ApprovalProvider>{children}</ApprovalProvider>

‎client/src/components/Chat/Messages/HoverButtons.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState, useMemo, useCallback, memo } from 'react';
22
import { Copy, Check } from 'lucide';
33
import { useAtomValue } from 'jotai';
44
import { useRecoilState } from 'recoil';
5+
import { revealedQueuedTurnFamily } from '@librechat/chat/react';
56
import { getSpeechText, findMessageById, isUserInitiatedCompaction } from 'librechat-data-provider';
67
import {
78
Button,
@@ -15,7 +16,6 @@ import type { TConversation, TMessage, TFeedback } from 'librechat-data-provider
1516
import { useMessagesIsSubmitting, useOptionalMessagesOperations } from '~/Providers';
1617
import { useGenerationsByLatest, useLocalize } from '~/hooks';
1718
import { hasEditablePart } from './Content/editableParts';
18-
import { revealedQueuedTurnFamily } from '~/store/steer';
1919
import { Fork } from '~/components/Conversations';
2020
import { hoverButtonClasses } from './styles';
2121
import MessageAudio from './MessageAudio';

0 commit comments

Comments
 (0)