Skip to content

Commit c3beba5

Browse files
committed
[Feat]: make chat component flexible
1 parent facd5e0 commit c3beba5

File tree

9 files changed

+436
-29
lines changed

9 files changed

+436
-29
lines changed

client/packages/lowcoder/src/comps/comps/chatComp/chatCompTypes.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,31 @@ import QuerySelectControl from "comps/controls/querySelectControl";
1010
const ModelTypeOptions = [
1111
{ label: "Direct LLM", value: "direct-llm" },
1212
{ label: "n8n Workflow", value: "n8n" },
13+
{ label: "Query", value: "query" },
1314
] as const;
1415

1516
export const chatChildrenMap = {
1617
text: withDefault(StringControl, "Chat Component Placeholder"),
1718
chatQuery: QuerySelectControl,
1819
currentMessage: stringExposingStateControl("currentMessage", ""),
19-
modelType: dropdownControl(ModelTypeOptions, "direct-llm"),
20+
modelType: dropdownControl(ModelTypeOptions, "query"),
2021
modelHost: withDefault(StringControl, ""),
2122
streaming: BoolControl.DEFAULT_TRUE,
2223
systemPrompt: withDefault(StringControl, "You are a helpful assistant."),
2324
agent: BoolControl,
2425
maxInteractions: withDefault(NumberControl, 10),
25-
tableName: withDefault(StringControl, ""),
26+
tableName: withDefault(StringControl, "default"),
2627
};
2728

2829
export type ChatCompProps = {
2930
text: string;
3031
chatQuery: string;
3132
currentMessage: string;
3233
modelType: string;
34+
modelHost: string;
3335
streaming: boolean;
3436
systemPrompt: string;
3537
agent: boolean;
3638
maxInteractions: number;
39+
tableName: string;
3740
};
Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx
2-
import React from "react";
2+
import React, { useMemo } from "react";
33
import { ChatCompProps } from "./chatCompTypes";
44
import { CompAction } from "lowcoder-core";
55
import { ChatApp } from "./components/ChatApp";
6+
import { createChatStorage } from './utils/chatStorageFactory';
67

78
import "@assistant-ui/styles/index.css";
89
import "@assistant-ui/styles/markdown.css";
@@ -13,8 +14,33 @@ interface ChatViewProps extends ChatCompProps {
1314
}
1415

1516
export const ChatView = React.memo((props: ChatViewProps) => {
16-
const { chatQuery, currentMessage, dispatch } = props;
17-
return <ChatApp chatQuery={chatQuery} currentMessage={currentMessage} dispatch={dispatch} />;
17+
const {
18+
chatQuery,
19+
currentMessage,
20+
dispatch,
21+
modelType,
22+
modelHost,
23+
systemPrompt,
24+
streaming,
25+
tableName
26+
} = props;
27+
28+
// Create storage instance based on tableName
29+
const storage = useMemo(() => createChatStorage(tableName || "default"), [tableName]);
30+
31+
return (
32+
<ChatApp
33+
chatQuery={chatQuery}
34+
currentMessage={currentMessage}
35+
dispatch={dispatch}
36+
modelType={modelType}
37+
modelHost={modelHost}
38+
systemPrompt={systemPrompt}
39+
streaming={streaming}
40+
tableName={tableName}
41+
storage={storage}
42+
/>
43+
);
1844
});
1945

2046
ChatView.displayName = 'ChatView';
Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,43 @@
11
import { ChatProvider } from "./context/ChatContext";
22
import { ChatMain } from "./ChatMain";
33
import { CompAction } from "lowcoder-core";
4+
import { createChatStorage } from "../utils/chatStorageFactory";
45

56
interface ChatAppProps {
67
chatQuery: string;
78
currentMessage: string;
89
dispatch?: (action: CompAction<any>) => void;
10+
modelType: string;
11+
modelHost?: string;
12+
systemPrompt?: string;
13+
streaming?: boolean;
14+
tableName: string;
15+
storage: ReturnType<typeof createChatStorage>;
916
}
1017

11-
export function ChatApp({ chatQuery, currentMessage, dispatch }: ChatAppProps) {
18+
export function ChatApp({
19+
chatQuery,
20+
currentMessage,
21+
dispatch,
22+
modelType,
23+
modelHost,
24+
systemPrompt,
25+
streaming,
26+
tableName,
27+
storage
28+
}: ChatAppProps) {
1229
return (
13-
<ChatProvider>
14-
<ChatMain chatQuery={chatQuery} currentMessage={currentMessage} dispatch={dispatch} />
30+
<ChatProvider storage={storage}>
31+
<ChatMain
32+
chatQuery={chatQuery}
33+
currentMessage={currentMessage}
34+
dispatch={dispatch}
35+
modelType={modelType}
36+
modelHost={modelHost}
37+
systemPrompt={systemPrompt}
38+
streaming={streaming}
39+
tableName={tableName}
40+
/>
1541
</ChatProvider>
1642
);
17-
}
43+
}

client/packages/lowcoder/src/comps/comps/chatComp/components/ChatMain.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import {
1818
import styled from "styled-components";
1919
import { routeByNameAction, executeQueryAction, CompAction, changeChildAction } from "lowcoder-core";
2020
import { getPromiseAfterDispatch } from "util/promiseUtils";
21+
// ADD THIS IMPORT:
22+
import { createResponseHandler } from '../utils/responseFactory';
23+
import { useMemo } from 'react'; // if not already imported
2124

2225
const ChatContainer = styled.div`
2326
display: flex;
@@ -95,16 +98,46 @@ const callQuery = async (
9598
}
9699
};
97100

101+
// AFTER:
98102
interface ChatMainProps {
99103
chatQuery: string;
100104
currentMessage: string;
101105
dispatch?: (action: CompAction<any>) => void;
106+
// Add new props for response handling
107+
modelType: string;
108+
modelHost?: string;
109+
systemPrompt?: string;
110+
streaming?: boolean;
111+
tableName: string;
102112
}
103113

104-
export function ChatMain({ chatQuery, currentMessage, dispatch }: ChatMainProps) {
114+
export function ChatMain({
115+
chatQuery,
116+
currentMessage,
117+
dispatch,
118+
modelType,
119+
modelHost,
120+
systemPrompt,
121+
streaming,
122+
tableName }: ChatMainProps) {
105123
const { state, actions } = useChatContext();
106124
const [isRunning, setIsRunning] = useState(false);
107125

126+
// Create response handler based on model type
127+
const responseHandler = useMemo(() => {
128+
const responseType = modelType === "n8n" ? "direct-api" : "query";
129+
130+
return createResponseHandler(responseType, {
131+
// Query handler config
132+
chatQuery,
133+
dispatch,
134+
// Direct API handler config
135+
modelHost,
136+
systemPrompt,
137+
streaming
138+
});
139+
}, [modelType, chatQuery, dispatch, modelHost, systemPrompt, streaming]);
140+
108141
console.log("STATE", state);
109142

110143
// Get messages for current thread
@@ -143,7 +176,7 @@ export function ChatMain({ chatQuery, currentMessage, dispatch }: ChatMainProps)
143176

144177
try {
145178
// Call selected query / fallback to mock
146-
const response = await callQuery(chatQuery, userMessage.text, dispatch);
179+
const response = await responseHandler.sendMessage(userMessage.text);
147180

148181
const assistantMessage: MyMessage = {
149182
id: generateId(),
@@ -201,7 +234,7 @@ export function ChatMain({ chatQuery, currentMessage, dispatch }: ChatMainProps)
201234
setIsRunning(true);
202235

203236
try {
204-
const response = await callQuery(chatQuery, editedMessage.text, dispatch);
237+
const response = await responseHandler.sendMessage(editedMessage.text);
205238

206239
const assistantMessage: MyMessage = {
207240
id: generateId(),

client/packages/lowcoder/src/comps/comps/chatComp/components/context/ChatContext.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { createContext, useContext, useReducer, useEffect, ReactNode } from "react";
2-
import { chatStorage, ThreadData as StoredThreadData } from "../../utils/chatStorage";
3-
2+
import { ThreadData as StoredThreadData } from "../../utils/chatStorageFactory";
43
// Define thread-specific message type
54
export interface MyMessage {
65
id: string;
@@ -176,18 +175,19 @@ interface ChatContextType {
176175
const ChatContext = createContext<ChatContextType | null>(null);
177176

178177
// Chat provider component
179-
export function ChatProvider({ children }: { children: ReactNode }) {
178+
export function ChatProvider({ children, storage }: { children: ReactNode, storage: ReturnType<typeof import("../../utils/chatStorageFactory").createChatStorage>;
179+
}) {
180180
const [state, dispatch] = useReducer(chatReducer, initialState);
181181

182182
// Initialize data from storage
183183
const initialize = async () => {
184184
dispatch({ type: "INITIALIZE_START" });
185185

186186
try {
187-
await chatStorage.initialize();
187+
await storage.initialize();
188188

189189
// Load all threads from storage
190-
const storedThreads = await chatStorage.getAllThreads();
190+
const storedThreads = await storage.getAllThreads();
191191

192192
if (storedThreads.length > 0) {
193193
// Convert stored threads to UI format
@@ -200,7 +200,7 @@ export function ChatProvider({ children }: { children: ReactNode }) {
200200
// Load messages for each thread
201201
const threadMessages = new Map<string, MyMessage[]>();
202202
for (const thread of storedThreads) {
203-
const messages = await chatStorage.getMessages(thread.threadId);
203+
const messages = await storage.getMessages(thread.threadId);
204204
threadMessages.set(thread.threadId, messages);
205205
}
206206

@@ -228,7 +228,7 @@ export function ChatProvider({ children }: { children: ReactNode }) {
228228
createdAt: Date.now(),
229229
updatedAt: Date.now(),
230230
};
231-
await chatStorage.saveThread(defaultThread);
231+
await storage.saveThread(defaultThread);
232232

233233
dispatch({
234234
type: "INITIALIZE_SUCCESS",
@@ -268,7 +268,7 @@ export function ChatProvider({ children }: { children: ReactNode }) {
268268
createdAt: Date.now(),
269269
updatedAt: Date.now(),
270270
};
271-
await chatStorage.saveThread(storedThread);
271+
await storage.saveThread(storedThread);
272272
dispatch({ type: "MARK_SAVED" });
273273
} catch (error) {
274274
console.error("Failed to save new thread:", error);
@@ -283,14 +283,14 @@ export function ChatProvider({ children }: { children: ReactNode }) {
283283

284284
// Save to storage
285285
try {
286-
const existingThread = await chatStorage.getThread(threadId);
286+
const existingThread = await storage.getThread(threadId);
287287
if (existingThread) {
288288
const updatedThread: StoredThreadData = {
289289
...existingThread,
290290
...updates,
291291
updatedAt: Date.now(),
292292
};
293-
await chatStorage.saveThread(updatedThread);
293+
await storage.saveThread(updatedThread);
294294
dispatch({ type: "MARK_SAVED" });
295295
}
296296
} catch (error) {
@@ -304,7 +304,7 @@ export function ChatProvider({ children }: { children: ReactNode }) {
304304

305305
// Delete from storage
306306
try {
307-
await chatStorage.deleteThread(threadId);
307+
await storage.deleteThread(threadId);
308308
dispatch({ type: "MARK_SAVED" });
309309
} catch (error) {
310310
console.error("Failed to delete thread:", error);
@@ -318,7 +318,7 @@ export function ChatProvider({ children }: { children: ReactNode }) {
318318

319319
// Save to storage
320320
try {
321-
await chatStorage.saveMessage(message, threadId);
321+
await storage.saveMessage(message, threadId);
322322
dispatch({ type: "MARK_SAVED" });
323323
} catch (error) {
324324
console.error("Failed to save message:", error);
@@ -331,7 +331,7 @@ export function ChatProvider({ children }: { children: ReactNode }) {
331331

332332
// Save to storage
333333
try {
334-
await chatStorage.saveMessages(messages, threadId);
334+
await storage.saveMessages(messages, threadId);
335335
dispatch({ type: "MARK_SAVED" });
336336
} catch (error) {
337337
console.error("Failed to save messages:", error);

0 commit comments

Comments
 (0)