|
| 1 | +import { createAsyncThunk } from "@reduxjs/toolkit/react"; |
| 2 | +import { AppDispatch, RootState } from "../../app/store"; |
| 3 | +import { CHAT_DB_THREADS_SUB } from "./consts"; |
| 4 | +import { consumeStream } from "../../features/Chat/Thread/utils"; |
| 5 | +import { |
| 6 | + isCThreadSubResponseUpdate, |
| 7 | + isCThreadSubResponseDelete, |
| 8 | +} from "./types"; |
| 9 | +import { chatDbActions } from "../../features/ChatDB/chatDbSlice"; |
| 10 | + |
| 11 | +const createAppAsyncThunk = createAsyncThunk.withTypes<{ |
| 12 | + state: RootState; |
| 13 | + dispatch: AppDispatch; |
| 14 | +}>(); |
| 15 | + |
| 16 | +export type SubscribeToThreadArgs = |
| 17 | + | { |
| 18 | + quick_search?: string; |
| 19 | + limit?: number; |
| 20 | + } |
| 21 | + | undefined; |
| 22 | +function subscribeToThreads( |
| 23 | + args: SubscribeToThreadArgs = {}, |
| 24 | + port = 8001, |
| 25 | + apiKey?: string | null, |
| 26 | + abortSignal?: AbortSignal, |
| 27 | +): Promise<Response> { |
| 28 | + const url = `http://127.0.0.1:${port}${CHAT_DB_THREADS_SUB}`; |
| 29 | + const headers = new Headers(); |
| 30 | + headers.append("Content-Type", "application/json"); |
| 31 | + if (apiKey) { |
| 32 | + headers.append("Authorization", `Bearer ${apiKey}`); |
| 33 | + } |
| 34 | + |
| 35 | + return fetch(url, { |
| 36 | + method: "POST", |
| 37 | + headers, |
| 38 | + redirect: "follow", |
| 39 | + cache: "no-cache", |
| 40 | + body: JSON.stringify(args), |
| 41 | + signal: abortSignal, |
| 42 | + }); |
| 43 | +} |
| 44 | + |
| 45 | +// type CThreadSubResponse = CThreadSubResponseUpdate | CThreadSubResponseDelete; |
| 46 | +// function isCThreadSubResponseChunk(value: unknown): value is CThreadSubResponse { |
| 47 | +// if (isCThreadSubResponseUpdate(value)) return true; |
| 48 | +// if (isCThreadSubResponseDelete(value)) return true; |
| 49 | +// return false; |
| 50 | +// } |
| 51 | + |
| 52 | +export const subscribeToThreadsThunk = createAppAsyncThunk< |
| 53 | + unknown, |
| 54 | + SubscribeToThreadArgs |
| 55 | +>("chatdbApi/subscribeToThreads", (args, thunkApi) => { |
| 56 | + const state = thunkApi.getState() as unknown as RootState; |
| 57 | + const port = state.config.lspPort; |
| 58 | + const apiKey = state.config.apiKey; |
| 59 | + return subscribeToThreads(args, port, apiKey, thunkApi.signal) |
| 60 | + .then((response) => { |
| 61 | + if (!response.ok) { |
| 62 | + throw new Error(response.statusText); |
| 63 | + } |
| 64 | + const reader = response.body?.getReader(); |
| 65 | + if (!reader) return; |
| 66 | + |
| 67 | + const onAbort = () => { |
| 68 | + // console.log("knowledge stream aborted"); |
| 69 | + }; |
| 70 | + |
| 71 | + const onChunk = (chunk: unknown) => { |
| 72 | + if (isCThreadSubResponseUpdate(chunk)) { |
| 73 | + const action = chatDbActions.updateCThread(chunk.cthread_rec); |
| 74 | + thunkApi.dispatch(action); |
| 75 | + // dispatch update |
| 76 | + } else if (isCThreadSubResponseDelete(chunk)) { |
| 77 | + const action = chatDbActions.deleteCThread(chunk.cthread_id); |
| 78 | + thunkApi.dispatch(action); |
| 79 | + // dispatch delete |
| 80 | + } else { |
| 81 | + console.log("unknown thread chunk", chunk); |
| 82 | + } |
| 83 | + }; |
| 84 | + |
| 85 | + return consumeStream(reader, thunkApi.signal, onAbort, onChunk); |
| 86 | + }) |
| 87 | + .catch((err) => { |
| 88 | + // eslint-disable-next-line no-console |
| 89 | + console.error("Error in chat thread subscription", err); |
| 90 | + }); |
| 91 | +}); |
| 92 | + |
| 93 | +// Types for the API |
| 94 | + |
| 95 | +// export interface CMessage { |
| 96 | +// cmessage_belongs_to_cthread_id: string; |
| 97 | +// cmessage_alt: number; |
| 98 | +// cmessage_num: number; |
| 99 | +// cmessage_prev_alt: number; |
| 100 | +// cmessage_usage_model: string; |
| 101 | +// cmessage_usage_prompt: number; |
| 102 | +// cmessage_usage_completion: number; |
| 103 | +// cmessage_json: string; |
| 104 | +// } |
| 105 | + |
| 106 | +// export interface Chore { |
| 107 | +// chore_id: string; |
| 108 | +// chore_title: string; |
| 109 | +// chore_spontaneous_work_enable: boolean; |
| 110 | +// chore_created_ts: number; |
| 111 | +// chore_archived_ts: number; |
| 112 | +// } |
| 113 | + |
| 114 | +// export interface ChoreEvent { |
| 115 | +// chore_event_id: string; |
| 116 | +// chore_event_belongs_to_chore_id: string; |
| 117 | +// chore_event_summary: string; |
| 118 | +// chore_event_ts: number; |
| 119 | +// chore_event_link: string; |
| 120 | +// chore_event_cthread_id: string | null; |
| 121 | +// } |
| 122 | + |
| 123 | +// // Request types |
| 124 | +// export interface CThreadSubscription { |
| 125 | +// quicksearch?: string; |
| 126 | +// limit?: number; |
| 127 | +// } |
| 128 | + |
| 129 | +// export interface CMessagesSubscription { |
| 130 | +// cmessage_belongs_to_cthread_id: string; |
| 131 | +// } |
| 132 | + |
| 133 | +// API definition |
| 134 | +// export const chatDbApi = createApi({ |
| 135 | +// reducerPath: "chatdbApi", |
| 136 | +// baseQuery: fetchBaseQuery({ |
| 137 | +// prepareHeaders: (headers, { getState }) => { |
| 138 | +// const token = (getState() as RootState).config.apiKey; |
| 139 | +// if (token) { |
| 140 | +// headers.set("Authorization", `Bearer ${token}`); |
| 141 | +// } |
| 142 | +// return headers; |
| 143 | +// }, |
| 144 | +// }), |
| 145 | +// endpoints: (builder) => ({ |
| 146 | +// // Threads |
| 147 | +// subscribeCThreads: builder.mutation<void, CThreadSubscription>({ |
| 148 | +// query: (subscription) => ({ |
| 149 | +// url: "/cthreads-sub", |
| 150 | +// method: "POST", |
| 151 | +// body: subscription, |
| 152 | +// }), |
| 153 | +// }), |
| 154 | +// updateCThread: builder.mutation< |
| 155 | +// { status: string; cthread: CThread }, |
| 156 | +// Partial<CThread> |
| 157 | +// >({ |
| 158 | +// query: (thread) => ({ |
| 159 | +// url: "/cthread-update", |
| 160 | +// method: "POST", |
| 161 | +// body: thread, |
| 162 | +// }), |
| 163 | +// }), |
| 164 | + |
| 165 | +// // Messages |
| 166 | +// subscribeCMessages: builder.mutation<void, CMessagesSubscription>({ |
| 167 | +// query: (subscription) => ({ |
| 168 | +// url: "/cmessages-sub", |
| 169 | +// method: "POST", |
| 170 | +// body: subscription, |
| 171 | +// }), |
| 172 | +// }), |
| 173 | +// updateCMessages: builder.mutation<{ status: string }, CMessage[]>({ |
| 174 | +// query: (messages) => ({ |
| 175 | +// url: "/cmessages-update", |
| 176 | +// method: "POST", |
| 177 | +// body: messages, |
| 178 | +// }), |
| 179 | +// }), |
| 180 | + |
| 181 | +// // Chores |
| 182 | +// subscribeChores: builder.mutation<void, void>({ |
| 183 | +// query: () => ({ |
| 184 | +// url: "/chores-sub", |
| 185 | +// method: "POST", |
| 186 | +// }), |
| 187 | +// }), |
| 188 | +// updateChore: builder.mutation<{ status: string }, Partial<Chore>>({ |
| 189 | +// query: (chore) => ({ |
| 190 | +// url: "/chore-update", |
| 191 | +// method: "POST", |
| 192 | +// body: chore, |
| 193 | +// }), |
| 194 | +// }), |
| 195 | +// updateChoreEvent: builder.mutation<{ status: string }, Partial<ChoreEvent>>( |
| 196 | +// { |
| 197 | +// query: (event) => ({ |
| 198 | +// url: "/chore-event-update", |
| 199 | +// method: "POST", |
| 200 | +// body: event, |
| 201 | +// }), |
| 202 | +// }, |
| 203 | +// ), |
| 204 | +// }), |
| 205 | +// }); |
| 206 | + |
| 207 | +// // Export hooks for usage in components |
| 208 | +// export const { |
| 209 | +// useSubscribeCThreadsMutation, |
| 210 | +// useUpdateCThreadMutation, |
| 211 | +// useSubscribeCMessagesMutation, |
| 212 | +// useUpdateCMessagesMutation, |
| 213 | +// useSubscribeChoresMutation, |
| 214 | +// useUpdateChoreMutation, |
| 215 | +// useUpdateChoreEventMutation, |
| 216 | +// } = chatDbApi; |
0 commit comments