Skip to content

Commit ff6b362

Browse files
committed
wip(chat db): get thread from the lsp.
1 parent 6d1a765 commit ff6b362

File tree

7 files changed

+430
-0
lines changed

7 files changed

+430
-0
lines changed

refact-agent/gui/src/app/store.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { knowledgeSlice } from "../features/Knowledge/knowledgeSlice";
5252
import { checkpointsSlice } from "../features/Checkpoints/checkpointsSlice";
5353
import { checkpointsApi } from "../services/refact/checkpoints";
5454
import { patchesAndDiffsTrackerSlice } from "../features/PatchesAndDiffsTracker/patchesAndDiffsTrackerSlice";
55+
import { chatDbSlice } from "../features/ChatDB/chatDbSlice";
5556

5657
const tipOfTheDayPersistConfig = {
5758
key: "totd",
@@ -115,6 +116,7 @@ const rootReducer = combineSlices(
115116
knowledgeSlice,
116117
checkpointsSlice,
117118
patchesAndDiffsTrackerSlice,
119+
chatDbSlice,
118120
);
119121

120122
const rootPersistConfig = {

refact-agent/gui/src/components/Sidebar/Sidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { deleteChatById } from "../../features/History/historySlice";
77
import { push } from "../../features/Pages/pagesSlice";
88
import { restoreChat, type ChatThread } from "../../features/Chat/Thread";
99
import { FeatureMenu } from "../../features/Config/FeatureMenu";
10+
import { subscribeToThreadsThunk } from "../../services/refact/chatdb";
1011

1112
export type SidebarProps = {
1213
takingNotes: boolean;
@@ -24,6 +25,7 @@ export type SidebarProps = {
2425
export const Sidebar: React.FC<SidebarProps> = ({ takingNotes, style }) => {
2526
// TODO: these can be lowered.
2627
const dispatch = useAppDispatch();
28+
void dispatch(subscribeToThreadsThunk());
2729
const history = useAppSelector((app) => app.history, {
2830
// TODO: selector issue here
2931
devModeChecks: { stabilityCheck: "never" },
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2+
import { CThread } from "../../services/refact";
3+
import { reset } from "../FIM";
4+
import { setError } from "../Errors/errorsSlice";
5+
import { getChatById } from "../History/historySlice";
6+
7+
export type ChatDbState = {
8+
loading: boolean;
9+
error: string | null;
10+
chats: Record<string, CThread>;
11+
};
12+
13+
const initialState: ChatDbState = {
14+
loading: false,
15+
error: null,
16+
chats: {},
17+
};
18+
19+
export const chatDbSlice = createSlice({
20+
name: "chatDb",
21+
initialState,
22+
reducers: {
23+
reset: () => initialState,
24+
setLoading: (state, action: PayloadAction<boolean>) => {
25+
state.loading = action.payload;
26+
},
27+
setError: (state, action: PayloadAction<string>) => {
28+
state.error = action.payload;
29+
},
30+
startLoading: (state) => {
31+
state.loading = true;
32+
state.error = null;
33+
state.chats = {};
34+
},
35+
updateCThread: (state, action: PayloadAction<CThread>) => {
36+
state.chats[action.payload.cthread_id] = action.payload;
37+
},
38+
deleteCThread: (state, action: PayloadAction<string>) => {
39+
if (action.payload in state.chats) {
40+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
41+
delete state.chats[action.payload];
42+
}
43+
},
44+
},
45+
selectors: {
46+
getChats: (state) => state.chats,
47+
getLoading: (state) => state.loading,
48+
getError: (state) => state.error,
49+
},
50+
});
51+
52+
export const chatDbActions = chatDbSlice.actions;
53+
export const chatDbSelectors = chatDbSlice.selectors;
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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;

refact-agent/gui/src/services/refact/consts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,5 @@ export const KNOWLEDGE_REMOVE_URL = "/v1/mem-erase";
4040
export const KNOWLEDGE_UPDATE_USED_URL = "/v1/mem-update-used";
4141
export const KNOWLEDGE_UPDATE_URL = "/v1/mem-upd";
4242
export const KNOWLEDGE_CREATE_URL = "/v1/trajectory-save";
43+
// Chatdblinks
44+
export const CHAT_DB_THREADS_SUB = "/db_v1/cthreads-sub";

refact-agent/gui/src/services/refact/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export * from "./integrations";
1313
export * from "./docker";
1414
export * from "./telemetry";
1515
export * from "./knowledge";
16+
export * from "./chatdb";

0 commit comments

Comments
 (0)