Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7c291df

Browse files
Merge pull request#1850 from iamfaran/feat/chat-component
[Feat] AI Chat Component
2 parents2014590 +4f9fbba commit7c291df

29 files changed

+3168
-1333
lines changed

‎client/packages/lowcoder/package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
"@jsonforms/core":"^3.5.1",
3434
"@lottiefiles/dotlottie-react":"^0.13.0",
3535
"@manaflair/redux-batch":"^1.0.0",
36+
"@radix-ui/react-avatar":"^1.1.10",
37+
"@radix-ui/react-dialog":"^1.1.14",
3638
"@radix-ui/react-slot":"^1.2.3",
3739
"@radix-ui/react-tooltip":"^1.2.7",
3840
"@rjsf/antd":"^5.24.9",

‎client/packages/lowcoder/src/components/ResCreatePanel.tsx‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { BottomResTypeEnum } from "types/bottomRes";
1313
import{LargeBottomResIconWrapper}from"util/bottomResUtils";
1414
importtype{PageType}from"../constants/pageConstants";
1515
importtype{SizeType}from"antd/es/config-provider/SizeContext";
16-
import{Datasource}from"constants/datasourceConstants";
16+
import{Datasource,QUICK_SSE_HTTP_API_ID}from"constants/datasourceConstants";
1717
import{
1818
QUICK_GRAPHQL_ID,
1919
QUICK_REST_API_ID,
@@ -172,13 +172,22 @@ const ResButton = (props: {
172172
compType:"streamApi",
173173
},
174174
},
175+
175176
alasql:{
176177
label:trans("query.quickAlasql"),
177178
type:BottomResTypeEnum.Query,
178179
extra:{
179180
compType:"alasql",
180181
},
181182
},
183+
sseHttpApi:{
184+
label:trans("query.quickSseHttpAPI"),
185+
type:BottomResTypeEnum.Query,
186+
extra:{
187+
compType:"sseHttpApi",
188+
dataSourceId:QUICK_SSE_HTTP_API_ID,
189+
},
190+
},
182191
graphql:{
183192
label:trans("query.quickGraphql"),
184193
type:BottomResTypeEnum.Query,
@@ -339,6 +348,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
339348
<DataSourceListWrapper$placement={placement}>
340349
<ResButtonsize={buttonSize}identifier={"restApi"}onSelect={onSelect}/>
341350
<ResButtonsize={buttonSize}identifier={"streamApi"}onSelect={onSelect}/>
351+
<ResButtonsize={buttonSize}identifier={"sseHttpApi"}onSelect={onSelect}/>
342352
<ResButtonsize={buttonSize}identifier={"alasql"}onSelect={onSelect}/>
343353
<ResButtonsize={buttonSize}identifier={"graphql"}onSelect={onSelect}/>
344354
<DataSourceButtonsize={buttonSize}onClick={()=>setCurlModalVisible(true)}>
Lines changed: 257 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,258 @@
1-
// client/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx
2-
import{UICompBuilder}from"comps/generators";
3-
import{NameConfig,withExposingConfigs}from"comps/generators/withExposing";
4-
import{chatChildrenMap}from"./chatCompTypes";
5-
import{ChatView}from"./chatView";
6-
import{ChatPropertyView}from"./chatPropertyView";
7-
import{useEffect,useState}from"react";
8-
import{changeChildAction}from"lowcoder-core";
9-
10-
// Build the component
11-
letChatTmpComp=newUICompBuilder(
12-
chatChildrenMap,
13-
(props,dispatch)=>{
14-
useEffect(()=>{
15-
if(Boolean(props.tableName))return;
16-
17-
// Generate a unique database name for this ChatApp instance
18-
constgenerateUniqueTableName=()=>{
19-
consttimestamp=Date.now();
20-
constrandomId=Math.random().toString(36).substring(2,15);
21-
return`TABLE_${timestamp}`;
22-
};
23-
24-
consttableName=generateUniqueTableName();
25-
dispatch(changeChildAction('tableName',tableName,true));
26-
},[props.tableName]);
27-
28-
if(!props.tableName){
29-
returnnull;// Don't render until we have a unique DB name
30-
}
31-
return<ChatView{...props}chatQuery={props.chatQuery.value}/>;
32-
}
33-
)
34-
.setPropertyViewFn((children)=><ChatPropertyViewchildren={children}/>)
35-
.build();
36-
37-
ChatTmpComp=classextendsChatTmpComp{
38-
overrideautoHeight():boolean{
39-
returnthis.children.autoHeight.getView();
40-
}
41-
};
42-
43-
// Export the component
44-
exportconstChatComp=withExposingConfigs(ChatTmpComp,[
45-
newNameConfig("text","Chat component text"),
1+
// client/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx
2+
3+
import{UICompBuilder}from"comps/generators";
4+
import{NameConfig,withExposingConfigs}from"comps/generators/withExposing";
5+
import{StringControl}from"comps/controls/codeControl";
6+
import{arrayObjectExposingStateControl,stringExposingStateControl}from"comps/controls/codeStateControl";
7+
import{withDefault}from"comps/generators";
8+
import{BoolControl}from"comps/controls/boolControl";
9+
import{dropdownControl}from"comps/controls/dropdownControl";
10+
importQuerySelectControlfrom"comps/controls/querySelectControl";
11+
import{eventHandlerControl,EventConfigType}from"comps/controls/eventHandlerControl";
12+
import{ChatCore}from"./components/ChatCore";
13+
import{ChatPropertyView}from"./chatPropertyView";
14+
import{createChatStorage}from"./utils/storageFactory";
15+
import{QueryHandler,createMessageHandler}from"./handlers/messageHandlers";
16+
import{useMemo,useRef,useEffect}from"react";
17+
import{changeChildAction}from"lowcoder-core";
18+
import{ChatMessage}from"./types/chatTypes";
19+
import{trans}from"i18n";
20+
21+
import"@assistant-ui/styles/index.css";
22+
import"@assistant-ui/styles/markdown.css";
23+
24+
// ============================================================================
25+
// CHAT-SPECIFIC EVENTS
26+
// ============================================================================
27+
28+
exportconstcomponentLoadEvent:EventConfigType={
29+
label:trans("chat.componentLoad"),
30+
value:"componentLoad",
31+
description:trans("chat.componentLoadDesc"),
32+
};
33+
34+
exportconstmessageSentEvent:EventConfigType={
35+
label:trans("chat.messageSent"),
36+
value:"messageSent",
37+
description:trans("chat.messageSentDesc"),
38+
};
39+
40+
exportconstmessageReceivedEvent:EventConfigType={
41+
label:trans("chat.messageReceived"),
42+
value:"messageReceived",
43+
description:trans("chat.messageReceivedDesc"),
44+
};
45+
46+
exportconstthreadCreatedEvent:EventConfigType={
47+
label:trans("chat.threadCreated"),
48+
value:"threadCreated",
49+
description:trans("chat.threadCreatedDesc"),
50+
};
51+
52+
exportconstthreadUpdatedEvent:EventConfigType={
53+
label:trans("chat.threadUpdated"),
54+
value:"threadUpdated",
55+
description:trans("chat.threadUpdatedDesc"),
56+
};
57+
58+
exportconstthreadDeletedEvent:EventConfigType={
59+
label:trans("chat.threadDeleted"),
60+
value:"threadDeleted",
61+
description:trans("chat.threadDeletedDesc"),
62+
};
63+
64+
constChatEventOptions=[
65+
componentLoadEvent,
66+
messageSentEvent,
67+
messageReceivedEvent,
68+
threadCreatedEvent,
69+
threadUpdatedEvent,
70+
threadDeletedEvent,
71+
]asconst;
72+
73+
exportconstChatEventHandlerControl=eventHandlerControl(ChatEventOptions);
74+
75+
// ============================================================================
76+
// SIMPLIFIED CHILDREN MAP - WITH EVENT HANDLERS
77+
// ============================================================================
78+
79+
80+
exportfunctionaddSystemPromptToHistory(
81+
conversationHistory:ChatMessage[],
82+
systemPrompt:string
83+
):Array<{role:string;content:string;timestamp:number}>{
84+
// Format conversation history for use in queries
85+
constformattedHistory=conversationHistory.map(msg=>({
86+
role:msg.role,
87+
content:msg.text,
88+
timestamp:msg.timestamp
89+
}));
90+
91+
// Create system message (always exists since we have default)
92+
constsystemMessage=[{
93+
role:"system"asconst,
94+
content:systemPrompt,
95+
timestamp:Date.now()-1000000// Ensure it's always first chronologically
96+
}];
97+
98+
// Return complete history with system prompt prepended
99+
return[...systemMessage, ...formattedHistory];
100+
}
101+
102+
103+
functiongenerateUniqueTableName():string{
104+
return`chat${Math.floor(1000+Math.random()*9000)}`;
105+
}
106+
107+
constModelTypeOptions=[
108+
{label:trans("chat.handlerTypeQuery"),value:"query"},
109+
{label:trans("chat.handlerTypeN8N"),value:"n8n"},
110+
]asconst;
111+
112+
exportconstchatChildrenMap={
113+
// Storage
114+
// Storage (add the hidden property here)
115+
_internalDbName:withDefault(StringControl,""),
116+
// Message Handler Configuration
117+
handlerType:dropdownControl(ModelTypeOptions,"query"),
118+
chatQuery:QuerySelectControl,// Only used for "query" type
119+
modelHost:withDefault(StringControl,""),// Only used for "n8n" type
120+
systemPrompt:withDefault(StringControl,trans("chat.defaultSystemPrompt")),
121+
streaming:BoolControl.DEFAULT_TRUE,
122+
123+
// UI Configuration
124+
placeholder:withDefault(StringControl,trans("chat.defaultPlaceholder")),
125+
126+
// Database Information (read-only)
127+
databaseName:withDefault(StringControl,""),
128+
129+
// Event Handlers
130+
onEvent:ChatEventHandlerControl,
131+
132+
// Exposed Variables (not shown in Property View)
133+
currentMessage:stringExposingStateControl("currentMessage",""),
134+
conversationHistory:stringExposingStateControl("conversationHistory","[]"),
135+
};
136+
137+
// ============================================================================
138+
// CLEAN CHATCOMP - USES NEW ARCHITECTURE
139+
// ============================================================================
140+
141+
constChatTmpComp=newUICompBuilder(
142+
chatChildrenMap,
143+
(props,dispatch)=>{
144+
145+
constuniqueTableName=useRef<string>();
146+
// Generate unique table name once (with persistence)
147+
if(!uniqueTableName.current){
148+
// Use persisted name if exists, otherwise generate new one
149+
uniqueTableName.current=props._internalDbName||generateUniqueTableName();
150+
151+
// Save the name for future refreshes
152+
if(!props._internalDbName){
153+
dispatch(changeChildAction("_internalDbName",uniqueTableName.current,false));
154+
}
155+
156+
// Update the database name in the props for display
157+
constdbName=`ChatDB_${uniqueTableName.current}`;
158+
dispatch(changeChildAction("databaseName",dbName,false));
159+
}
160+
// Create storage with unique table name
161+
conststorage=useMemo(()=>
162+
createChatStorage(uniqueTableName.current!),
163+
[]
164+
);
165+
166+
// Create message handler based on type
167+
constmessageHandler=useMemo(()=>{
168+
consthandlerType=props.handlerType;
169+
170+
if(handlerType==="query"){
171+
returnnewQueryHandler({
172+
chatQuery:props.chatQuery.value,
173+
dispatch,
174+
streaming:props.streaming,
175+
});
176+
}elseif(handlerType==="n8n"){
177+
returncreateMessageHandler("n8n",{
178+
modelHost:props.modelHost,
179+
systemPrompt:props.systemPrompt,
180+
streaming:props.streaming
181+
});
182+
}else{
183+
// Fallback to mock handler
184+
returncreateMessageHandler("mock",{
185+
chatQuery:props.chatQuery.value,
186+
dispatch,
187+
streaming:props.streaming
188+
});
189+
}
190+
},[
191+
props.handlerType,
192+
props.chatQuery,
193+
props.modelHost,
194+
props.systemPrompt,
195+
props.streaming,
196+
dispatch,
197+
]);
198+
199+
// Handle message updates for exposed variable
200+
consthandleMessageUpdate=(message:string)=>{
201+
dispatch(changeChildAction("currentMessage",message,false));
202+
// Trigger messageSent event
203+
props.onEvent("messageSent");
204+
};
205+
206+
// Handle conversation history updates for exposed variable
207+
// Handle conversation history updates for exposed variable
208+
consthandleConversationUpdate=(conversationHistory:any[])=>{
209+
// Use utility function to create complete history with system prompt
210+
consthistoryWithSystemPrompt=addSystemPromptToHistory(
211+
conversationHistory,
212+
props.systemPrompt
213+
);
214+
215+
// Expose the complete history (with system prompt) for use in queries
216+
dispatch(changeChildAction("conversationHistory",JSON.stringify(historyWithSystemPrompt),false));
217+
218+
// Trigger messageReceived event when bot responds
219+
constlastMessage=conversationHistory[conversationHistory.length-1];
220+
if(lastMessage&&lastMessage.role==='assistant'){
221+
props.onEvent("messageReceived");
222+
}
223+
};
224+
225+
// Cleanup on unmount
226+
useEffect(()=>{
227+
return()=>{
228+
consttableName=uniqueTableName.current;
229+
if(tableName){
230+
storage.cleanup();
231+
}
232+
};
233+
},[]);
234+
235+
return(
236+
<ChatCore
237+
storage={storage}
238+
messageHandler={messageHandler}
239+
placeholder={props.placeholder}
240+
onMessageUpdate={handleMessageUpdate}
241+
onConversationUpdate={handleConversationUpdate}
242+
onEvent={props.onEvent}
243+
/>
244+
);
245+
}
246+
)
247+
.setPropertyViewFn((children)=><ChatPropertyViewchildren={children}/>)
248+
.build();
249+
250+
// ============================================================================
251+
// EXPORT WITH EXPOSED VARIABLES
252+
// ============================================================================
253+
254+
exportconstChatComp=withExposingConfigs(ChatTmpComp,[
255+
newNameConfig("currentMessage","Current user message"),
256+
newNameConfig("conversationHistory","Full conversation history as JSON array (includes system prompt for API calls)"),
257+
newNameConfig("databaseName","Database name for SQL queries (ChatDB_<componentName>)"),
46258
]);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp