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

Commit85f8df1

Browse files
Merge pull request#1841 from iamfaran/feat/chat-component
[In Progress] [Feat]: Chat Component
2 parents6ee73d2 +be3b3ab commit85f8df1

File tree

21 files changed

+2626
-9
lines changed

21 files changed

+2626
-9
lines changed

‎client/packages/lowcoder/package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
"main":"src/index.sdk.ts",
77
"types":"src/index.sdk.ts",
88
"dependencies": {
9+
"@ai-sdk/openai":"^1.3.22",
910
"@ant-design/icons":"^5.3.0",
11+
"@assistant-ui/react":"^0.10.24",
12+
"@assistant-ui/react-ai-sdk":"^0.10.14",
13+
"@assistant-ui/react-markdown":"^0.10.5",
14+
"@assistant-ui/styles":"^0.1.13",
1015
"@bany/curl-to-json":"^1.2.8",
1116
"@codemirror/autocomplete":"^6.11.1",
1217
"@codemirror/commands":"^6.3.2",
@@ -28,6 +33,8 @@
2833
"@jsonforms/core":"^3.5.1",
2934
"@lottiefiles/dotlottie-react":"^0.13.0",
3035
"@manaflair/redux-batch":"^1.0.0",
36+
"@radix-ui/react-slot":"^1.2.3",
37+
"@radix-ui/react-tooltip":"^1.2.7",
3138
"@rjsf/antd":"^5.24.9",
3239
"@rjsf/core":"^5.24.9",
3340
"@rjsf/utils":"^5.24.9",
@@ -37,11 +44,13 @@
3744
"@types/react-signature-canvas":"^1.0.2",
3845
"@types/react-test-renderer":"^18.0.0",
3946
"@types/react-virtualized":"^9.21.21",
47+
"ai":"^4.3.16",
4048
"alasql":"^4.6.6",
4149
"animate.css":"^4.1.1",
4250
"antd":"^5.25.2",
4351
"axios":"^1.7.7",
4452
"buffer":"^6.0.3",
53+
"class-variance-authority":"^0.7.1",
4554
"clsx":"^2.0.0",
4655
"cnchar":"^3.2.4",
4756
"coolshapes-react":"lowcoder-org/coolshapes-react",
@@ -61,6 +70,7 @@
6170
"loglevel":"^1.8.0",
6271
"lowcoder-core":"workspace:^",
6372
"lowcoder-design":"workspace:^",
73+
"lucide-react":"^0.525.0",
6474
"mime":"^3.0.0",
6575
"moment":"^2.29.4",
6676
"numbro":"^2.3.6",
@@ -98,7 +108,7 @@
98108
"regenerator-runtime":"^0.13.9",
99109
"rehype-raw":"^6.1.1",
100110
"rehype-sanitize":"^5.0.1",
101-
"remark-gfm":"^4.0.0",
111+
"remark-gfm":"^4.0.1",
102112
"resize-observer-polyfill":"^1.5.1",
103113
"simplebar-react":"^3.2.4",
104114
"sql-formatter":"^8.2.0",
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
8+
// Build the component
9+
constChatTmpComp=newUICompBuilder(
10+
chatChildrenMap,
11+
(props)=><ChatView{...props}chatQuery={props.chatQuery.value}/>
12+
)
13+
.setPropertyViewFn((children)=><ChatPropertyViewchildren={children}/>)
14+
.build();
15+
16+
// Export the component
17+
exportconstChatComp=withExposingConfigs(ChatTmpComp,[
18+
newNameConfig("text","Chat component text"),
19+
]);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// client/packages/lowcoder/src/comps/comps/chatComp/chatCompTypes.ts
2+
import{StringControl,NumberControl}from"comps/controls/codeControl";
3+
import{withDefault}from"comps/generators";
4+
import{BoolControl}from"comps/controls/boolControl";
5+
import{dropdownControl}from"comps/controls/dropdownControl";
6+
importQuerySelectControlfrom"comps/controls/querySelectControl";
7+
8+
// Model type dropdown options
9+
constModelTypeOptions=[
10+
{label:"Direct LLM",value:"direct-llm"},
11+
{label:"n8n Workflow",value:"n8n"},
12+
]asconst;
13+
14+
exportconstchatChildrenMap={
15+
text:withDefault(StringControl,"Chat Component Placeholder"),
16+
chatQuery:QuerySelectControl,
17+
modelType:dropdownControl(ModelTypeOptions,"direct-llm"),
18+
streaming:BoolControl.DEFAULT_TRUE,
19+
systemPrompt:withDefault(StringControl,"You are a helpful assistant."),
20+
agent:BoolControl,
21+
maxInteractions:withDefault(NumberControl,10),
22+
};
23+
24+
exporttypeChatCompProps={
25+
text:string;
26+
chatQuery:string;
27+
modelType:string;
28+
streaming:boolean;
29+
systemPrompt:string;
30+
agent:boolean;
31+
maxInteractions:number;
32+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// client/packages/lowcoder/src/comps/comps/chatComp/chatPropertyView.tsx
2+
importReactfrom"react";
3+
import{Section,sectionNames}from"lowcoder-design";
4+
5+
exportconstChatPropertyView=React.memo((props:any)=>{
6+
const{ children}=props;
7+
8+
return(
9+
<Sectionname={sectionNames.basic}>
10+
{children.text.propertyView({label:"Text"})}
11+
{children.chatQuery.propertyView({label:"Chat Query"})}
12+
{children.modelType.propertyView({label:"Model Type"})}
13+
{children.streaming.propertyView({label:"Enable Streaming"})}
14+
{children.systemPrompt.propertyView({
15+
label:"System Prompt",
16+
placeholder:"Enter system prompt...",
17+
enableSpellCheck:false,
18+
})}
19+
{children.agent.propertyView({label:"Enable Agent Mode"})}
20+
{children.maxInteractions.propertyView({
21+
label:"Max Interactions",
22+
placeholder:"10",
23+
})}
24+
</Section>
25+
);
26+
});
27+
28+
ChatPropertyView.displayName='ChatPropertyView';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx
2+
importReactfrom"react";
3+
import{ChatCompProps}from"./chatCompTypes";
4+
import{ChatApp}from"./components/ChatApp";
5+
6+
import"@assistant-ui/styles/index.css";
7+
import"@assistant-ui/styles/markdown.css";
8+
9+
exportconstChatView=React.memo((props:ChatCompProps)=>{
10+
return<ChatApp/>;
11+
});
12+
13+
ChatView.displayName='ChatView';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import{ChatProvider}from"./context/ChatContext";
2+
import{ChatMain}from"./ChatMain";
3+
4+
exportfunctionChatApp(){
5+
return(
6+
<ChatProvider>
7+
<ChatMain/>
8+
</ChatProvider>
9+
);
10+
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
importReact,{useState}from"react";
2+
import{
3+
useExternalStoreRuntime,
4+
ThreadMessageLike,
5+
AppendMessage,
6+
AssistantRuntimeProvider,
7+
ExternalStoreThreadListAdapter,
8+
}from"@assistant-ui/react";
9+
import{Thread}from"./assistant-ui/thread";
10+
import{ThreadList}from"./assistant-ui/thread-list";
11+
import{
12+
useChatContext,
13+
MyMessage,
14+
ThreadData,
15+
RegularThreadData,
16+
ArchivedThreadData
17+
}from"./context/ChatContext";
18+
importstyledfrom"styled-components";
19+
20+
constChatContainer=styled.div`
21+
display: flex;
22+
height: 500px;
23+
24+
.aui-thread-list-root {
25+
width: 250px;
26+
background-color: #fff;
27+
padding: 10px;
28+
}
29+
30+
.aui-thread-root {
31+
flex: 1;
32+
background-color: #f9fafb;
33+
}
34+
35+
.aui-thread-list-item {
36+
cursor: pointer;
37+
transition: background-color 0.2s ease;
38+
39+
&[data-active="true"] {
40+
background-color: #dbeafe;
41+
border: 1px solid #bfdbfe;
42+
}
43+
}
44+
`;
45+
46+
constgenerateId=()=>Math.random().toString(36).substr(2,9);
47+
48+
constcallYourAPI=async(text:string)=>{
49+
// Simulate API delay
50+
awaitnewPromise(resolve=>setTimeout(resolve,1500));
51+
52+
// Simple responses
53+
return{
54+
content:"This is a mock response from your backend. You typed: "+text
55+
};
56+
};
57+
58+
exportfunctionChatMain(){
59+
const{ state, actions}=useChatContext();
60+
const[isRunning,setIsRunning]=useState(false);
61+
62+
console.log("STATE",state);
63+
64+
// Get messages for current thread
65+
constcurrentMessages=actions.getCurrentMessages();
66+
67+
// Convert custom format to ThreadMessageLike
68+
constconvertMessage=(message:MyMessage):ThreadMessageLike=>({
69+
role:message.role,
70+
content:[{type:"text",text:message.text}],
71+
id:message.id,
72+
createdAt:newDate(message.timestamp),
73+
});
74+
75+
constonNew=async(message:AppendMessage)=>{
76+
// Extract text from AppendMessage content array
77+
if(message.content.length!==1||message.content[0]?.type!=="text"){
78+
thrownewError("Only text content is supported");
79+
}
80+
81+
// Add user message in custom format
82+
constuserMessage:MyMessage={
83+
id:generateId(),
84+
role:"user",
85+
text:message.content[0].text,
86+
timestamp:Date.now(),
87+
};
88+
89+
// Update current thread with new user message
90+
awaitactions.addMessage(state.currentThreadId,userMessage);
91+
setIsRunning(true);
92+
93+
try{
94+
// Call mock API
95+
constresponse=awaitcallYourAPI(userMessage.text);
96+
97+
constassistantMessage:MyMessage={
98+
id:generateId(),
99+
role:"assistant",
100+
text:response.content,
101+
timestamp:Date.now(),
102+
};
103+
104+
// Update current thread with assistant response
105+
awaitactions.addMessage(state.currentThreadId,assistantMessage);
106+
}catch(error){
107+
// Handle errors gracefully
108+
consterrorMessage:MyMessage={
109+
id:generateId(),
110+
role:"assistant",
111+
text:`Sorry, I encountered an error:${errorinstanceofError ?error.message :'Unknown error'}`,
112+
timestamp:Date.now(),
113+
};
114+
115+
awaitactions.addMessage(state.currentThreadId,errorMessage);
116+
}finally{
117+
setIsRunning(false);
118+
}
119+
};
120+
121+
// Add onEdit functionality
122+
constonEdit=async(message:AppendMessage)=>{
123+
// Extract text from AppendMessage content array
124+
if(message.content.length!==1||message.content[0]?.type!=="text"){
125+
thrownewError("Only text content is supported");
126+
}
127+
128+
// Find the index where to insert the edited message
129+
constindex=currentMessages.findIndex((m)=>m.id===message.parentId)+1;
130+
131+
// Keep messages up to the parent
132+
constnewMessages=[...currentMessages.slice(0,index)];
133+
134+
// Add the edited message in custom format
135+
consteditedMessage:MyMessage={
136+
id:generateId(),
137+
role:"user",
138+
text:message.content[0].text,
139+
timestamp:Date.now(),
140+
};
141+
newMessages.push(editedMessage);
142+
143+
// Update messages using the new context action
144+
awaitactions.updateMessages(state.currentThreadId,newMessages);
145+
setIsRunning(true);
146+
147+
try{
148+
// Generate new response
149+
constresponse=awaitcallYourAPI(editedMessage.text);
150+
151+
constassistantMessage:MyMessage={
152+
id:generateId(),
153+
role:"assistant",
154+
text:response.content,
155+
timestamp:Date.now(),
156+
};
157+
158+
newMessages.push(assistantMessage);
159+
awaitactions.updateMessages(state.currentThreadId,newMessages);
160+
}catch(error){
161+
// Handle errors gracefully
162+
consterrorMessage:MyMessage={
163+
id:generateId(),
164+
role:"assistant",
165+
text:`Sorry, I encountered an error:${errorinstanceofError ?error.message :'Unknown error'}`,
166+
timestamp:Date.now(),
167+
};
168+
169+
newMessages.push(errorMessage);
170+
awaitactions.updateMessages(state.currentThreadId,newMessages);
171+
}finally{
172+
setIsRunning(false);
173+
}
174+
};
175+
176+
// Thread list adapter for managing multiple threads
177+
constthreadListAdapter:ExternalStoreThreadListAdapter={
178+
threadId:state.currentThreadId,
179+
threads:state.threadList.filter((t):t isRegularThreadData=>t.status==="regular"),
180+
archivedThreads:state.threadList.filter((t):t isArchivedThreadData=>t.status==="archived"),
181+
182+
onSwitchToNewThread:async()=>{
183+
constthreadId=awaitactions.createThread("New Chat");
184+
actions.setCurrentThread(threadId);
185+
},
186+
187+
onSwitchToThread:(threadId)=>{
188+
actions.setCurrentThread(threadId);
189+
},
190+
191+
onRename:async(threadId,newTitle)=>{
192+
awaitactions.updateThread(threadId,{title:newTitle});
193+
},
194+
195+
onArchive:async(threadId)=>{
196+
awaitactions.updateThread(threadId,{status:"archived"});
197+
},
198+
199+
onDelete:async(threadId)=>{
200+
awaitactions.deleteThread(threadId);
201+
},
202+
};
203+
204+
construntime=useExternalStoreRuntime({
205+
messages:currentMessages,
206+
setMessages:(messages)=>{
207+
actions.updateMessages(state.currentThreadId,messages);
208+
},
209+
convertMessage,
210+
isRunning,
211+
onNew,
212+
onEdit,
213+
adapters:{
214+
threadList:threadListAdapter,
215+
},
216+
});
217+
218+
if(!state.isInitialized){
219+
return<div>Loading...</div>;
220+
}
221+
222+
return(
223+
<AssistantRuntimeProviderruntime={runtime}>
224+
<ChatContainer>
225+
<ThreadList/>
226+
<Thread/>
227+
</ChatContainer>
228+
</AssistantRuntimeProvider>
229+
);
230+
}
231+

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp