| 15 | ); |
| 16 | |
| 17 | export const createChatStore = (config: CreateChatStoreConfig) => { |
| 18 | const { storage, llm } = config; |
| 19 | const { thread: threadStorage } = storage; |
| 20 | |
| 21 | const store = createStore<ChatStore>()( |
| 22 | subscribeWithSelector((set, get) => ({ |
| 23 | // Thread List State |
| 24 | threads: [], |
| 25 | isLoadingThreads: false, |
| 26 | threadListError: null, |
| 27 | selectedThreadId: null, |
| 28 | hasMoreThreads: false, |
| 29 | _nextCursor: undefined, |
| 30 | |
| 31 | // Thread State |
| 32 | messages: [], |
| 33 | isRunning: false, |
| 34 | isLoadingMessages: false, |
| 35 | threadError: null, |
| 36 | executingToolCallIds: new Set<string>(), |
| 37 | _abortController: null, |
| 38 | |
| 39 | // ── Thread List Actions ── |
| 40 | |
| 41 | loadThreads: () => { |
| 42 | set({ isLoadingThreads: true, threadListError: null }); |
| 43 | threadStorage |
| 44 | .listThreads(undefined) |
| 45 | .then(({ threads = [], nextCursor }) => { |
| 46 | set({ |
| 47 | threads, |
| 48 | isLoadingThreads: false, |
| 49 | _nextCursor: nextCursor, |
| 50 | hasMoreThreads: nextCursor !== undefined, |
| 51 | }); |
| 52 | }) |
| 53 | .catch((e) => { |
| 54 | set({ isLoadingThreads: false, threadListError: e }); |
| 55 | }); |
| 56 | }, |
| 57 | |
| 58 | loadMoreThreads: () => { |
| 59 | const cursor = get()._nextCursor; |
| 60 | if (cursor === undefined) return; |
| 61 | threadStorage |
| 62 | .listThreads(cursor) |
| 63 | .then(({ threads = [], nextCursor }) => { |
| 64 | set((s) => ({ |
| 65 | threads: mergeThreadList(s.threads, threads), |
| 66 | _nextCursor: nextCursor, |
| 67 | hasMoreThreads: nextCursor !== undefined, |
| 68 | })); |
| 69 | }) |
| 70 | .catch((e) => { |
| 71 | set({ threadListError: e }); |
| 72 | }); |
| 73 | }, |
| 74 | |