- Notifications
You must be signed in to change notification settings - Fork255
[In Progress] [Feat]: Chat Component#1841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Merged
raheeliftikhar5 merged 20 commits intolowcoder-org:feat/assistantfromiamfaran:feat/chat-componentJul 5, 2025
Uh oh!
There was an error while loading.Please reload this page.
Merged
Changes fromall commits
Commits
Show all changes
20 commits Select commitHold shift + click to select a range
d5d9873
init chat component
iamfarand1d9b92
refactor component structure
iamfaran0c132c1
install dependencies
iamfaran4e7e540
add assistant-ui components
iamfaranc541393
fix linter error
iamfaranbf3810f
add thread
iamfaran11c98fd
add properties for chat component
iamfaranaec9485
add query / new hook from assisstant ui
iamfaranb820f8e
use mock data
iamfaran0dc85d4
add edit functionality
iamfaranfd9dc77
fix message json issue
iamfaran22934c6
Merge branch 'dev' of github.com:lowcoder-org/lowcoder into feat/chat…
iamfaran52cdcf2
add threads logic
iamfaranc619f89
add alaSql to chat component
iamfaranf379cac
delete myruntime provider
iamfarane553d52
add storage support
iamfarandbd901c
add delete thread functionality
iamfaranbf9f269
add rename thread ability
iamfaran0205c72
[Feat]: Add chat component
iamfaranbe3b3ab
Merge branch 'dev' of github.com:lowcoder-org/lowcoder into feat/chat…
iamfaranFile filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
12 changes: 11 additions & 1 deletionclient/packages/lowcoder/package.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// client/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx | ||
import { UICompBuilder } from "comps/generators"; | ||
import { NameConfig, withExposingConfigs } from "comps/generators/withExposing"; | ||
import { chatChildrenMap } from "./chatCompTypes"; | ||
import { ChatView } from "./chatView"; | ||
import { ChatPropertyView } from "./chatPropertyView"; | ||
// Build the component | ||
const ChatTmpComp = new UICompBuilder( | ||
chatChildrenMap, | ||
(props) => <ChatView {...props} chatQuery={props.chatQuery.value} /> | ||
) | ||
.setPropertyViewFn((children) => <ChatPropertyView children={children} />) | ||
.build(); | ||
// Export the component | ||
export const ChatComp = withExposingConfigs(ChatTmpComp, [ | ||
new NameConfig("text", "Chat component text"), | ||
]); |
32 changes: 32 additions & 0 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/chatCompTypes.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// client/packages/lowcoder/src/comps/comps/chatComp/chatCompTypes.ts | ||
import { StringControl, NumberControl } from "comps/controls/codeControl"; | ||
import { withDefault } from "comps/generators"; | ||
import { BoolControl } from "comps/controls/boolControl"; | ||
import { dropdownControl } from "comps/controls/dropdownControl"; | ||
import QuerySelectControl from "comps/controls/querySelectControl"; | ||
// Model type dropdown options | ||
const ModelTypeOptions = [ | ||
{ label: "Direct LLM", value: "direct-llm" }, | ||
{ label: "n8n Workflow", value: "n8n" }, | ||
] as const; | ||
export const chatChildrenMap = { | ||
text: withDefault(StringControl, "Chat Component Placeholder"), | ||
chatQuery: QuerySelectControl, | ||
modelType: dropdownControl(ModelTypeOptions, "direct-llm"), | ||
streaming: BoolControl.DEFAULT_TRUE, | ||
systemPrompt: withDefault(StringControl, "You are a helpful assistant."), | ||
agent: BoolControl, | ||
maxInteractions: withDefault(NumberControl, 10), | ||
}; | ||
export type ChatCompProps = { | ||
text: string; | ||
chatQuery: string; | ||
modelType: string; | ||
streaming: boolean; | ||
systemPrompt: string; | ||
agent: boolean; | ||
maxInteractions: number; | ||
}; |
28 changes: 28 additions & 0 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/chatPropertyView.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// client/packages/lowcoder/src/comps/comps/chatComp/chatPropertyView.tsx | ||
import React from "react"; | ||
import { Section, sectionNames } from "lowcoder-design"; | ||
export const ChatPropertyView = React.memo((props: any) => { | ||
const { children } = props; | ||
return ( | ||
<Section name={sectionNames.basic}> | ||
{children.text.propertyView({ label: "Text" })} | ||
{children.chatQuery.propertyView({ label: "Chat Query" })} | ||
{children.modelType.propertyView({ label: "Model Type" })} | ||
{children.streaming.propertyView({ label: "Enable Streaming" })} | ||
{children.systemPrompt.propertyView({ | ||
label: "System Prompt", | ||
placeholder: "Enter system prompt...", | ||
enableSpellCheck: false, | ||
})} | ||
{children.agent.propertyView({ label: "Enable Agent Mode" })} | ||
{children.maxInteractions.propertyView({ | ||
label: "Max Interactions", | ||
placeholder: "10", | ||
})} | ||
</Section> | ||
); | ||
}); | ||
ChatPropertyView.displayName = 'ChatPropertyView'; |
13 changes: 13 additions & 0 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx | ||
import React from "react"; | ||
import { ChatCompProps } from "./chatCompTypes"; | ||
import { ChatApp } from "./components/ChatApp"; | ||
import "@assistant-ui/styles/index.css"; | ||
import "@assistant-ui/styles/markdown.css"; | ||
export const ChatView = React.memo((props: ChatCompProps) => { | ||
return <ChatApp />; | ||
}); | ||
ChatView.displayName = 'ChatView'; |
10 changes: 10 additions & 0 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/components/ChatApp.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ChatProvider } from "./context/ChatContext"; | ||
import { ChatMain } from "./ChatMain"; | ||
export function ChatApp() { | ||
return ( | ||
<ChatProvider> | ||
<ChatMain /> | ||
</ChatProvider> | ||
); | ||
} |
231 changes: 231 additions & 0 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/components/ChatMain.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
import React, { useState } from "react"; | ||
import { | ||
useExternalStoreRuntime, | ||
ThreadMessageLike, | ||
AppendMessage, | ||
AssistantRuntimeProvider, | ||
ExternalStoreThreadListAdapter, | ||
} from "@assistant-ui/react"; | ||
import { Thread } from "./assistant-ui/thread"; | ||
import { ThreadList } from "./assistant-ui/thread-list"; | ||
import { | ||
useChatContext, | ||
MyMessage, | ||
ThreadData, | ||
RegularThreadData, | ||
ArchivedThreadData | ||
} from "./context/ChatContext"; | ||
import styled from "styled-components"; | ||
const ChatContainer = styled.div` | ||
display: flex; | ||
height: 500px; | ||
.aui-thread-list-root { | ||
width: 250px; | ||
background-color: #fff; | ||
padding: 10px; | ||
} | ||
.aui-thread-root { | ||
flex: 1; | ||
background-color: #f9fafb; | ||
} | ||
.aui-thread-list-item { | ||
cursor: pointer; | ||
transition: background-color 0.2s ease; | ||
&[data-active="true"] { | ||
background-color: #dbeafe; | ||
border: 1px solid #bfdbfe; | ||
} | ||
} | ||
`; | ||
const generateId = () => Math.random().toString(36).substr(2, 9); | ||
const callYourAPI = async (text: string) => { | ||
// Simulate API delay | ||
await new Promise(resolve => setTimeout(resolve, 1500)); | ||
// Simple responses | ||
return { | ||
content: "This is a mock response from your backend. You typed: " + text | ||
}; | ||
}; | ||
export function ChatMain() { | ||
const { state, actions } = useChatContext(); | ||
const [isRunning, setIsRunning] = useState(false); | ||
console.log("STATE", state); | ||
// Get messages for current thread | ||
const currentMessages = actions.getCurrentMessages(); | ||
// Convert custom format to ThreadMessageLike | ||
const convertMessage = (message: MyMessage): ThreadMessageLike => ({ | ||
role: message.role, | ||
content: [{ type: "text", text: message.text }], | ||
id: message.id, | ||
createdAt: new Date(message.timestamp), | ||
}); | ||
const onNew = async (message: AppendMessage) => { | ||
// Extract text from AppendMessage content array | ||
if (message.content.length !== 1 || message.content[0]?.type !== "text") { | ||
throw new Error("Only text content is supported"); | ||
} | ||
// Add user message in custom format | ||
const userMessage: MyMessage = { | ||
id: generateId(), | ||
role: "user", | ||
text: message.content[0].text, | ||
timestamp: Date.now(), | ||
}; | ||
// Update current thread with new user message | ||
await actions.addMessage(state.currentThreadId, userMessage); | ||
setIsRunning(true); | ||
try { | ||
// Call mock API | ||
const response = await callYourAPI(userMessage.text); | ||
const assistantMessage: MyMessage = { | ||
id: generateId(), | ||
role: "assistant", | ||
text: response.content, | ||
timestamp: Date.now(), | ||
}; | ||
// Update current thread with assistant response | ||
await actions.addMessage(state.currentThreadId, assistantMessage); | ||
} catch (error) { | ||
// Handle errors gracefully | ||
const errorMessage: MyMessage = { | ||
id: generateId(), | ||
role: "assistant", | ||
text: `Sorry, I encountered an error: ${error instanceof Error ? error.message : 'Unknown error'}`, | ||
timestamp: Date.now(), | ||
}; | ||
await actions.addMessage(state.currentThreadId, errorMessage); | ||
} finally { | ||
setIsRunning(false); | ||
} | ||
}; | ||
// Add onEdit functionality | ||
const onEdit = async (message: AppendMessage) => { | ||
// Extract text from AppendMessage content array | ||
if (message.content.length !== 1 || message.content[0]?.type !== "text") { | ||
throw new Error("Only text content is supported"); | ||
} | ||
// Find the index where to insert the edited message | ||
const index = currentMessages.findIndex((m) => m.id === message.parentId) + 1; | ||
// Keep messages up to the parent | ||
const newMessages = [...currentMessages.slice(0, index)]; | ||
// Add the edited message in custom format | ||
const editedMessage: MyMessage = { | ||
id: generateId(), | ||
role: "user", | ||
text: message.content[0].text, | ||
timestamp: Date.now(), | ||
}; | ||
newMessages.push(editedMessage); | ||
// Update messages using the new context action | ||
await actions.updateMessages(state.currentThreadId, newMessages); | ||
setIsRunning(true); | ||
try { | ||
// Generate new response | ||
const response = await callYourAPI(editedMessage.text); | ||
const assistantMessage: MyMessage = { | ||
id: generateId(), | ||
role: "assistant", | ||
text: response.content, | ||
timestamp: Date.now(), | ||
}; | ||
newMessages.push(assistantMessage); | ||
await actions.updateMessages(state.currentThreadId, newMessages); | ||
} catch (error) { | ||
// Handle errors gracefully | ||
const errorMessage: MyMessage = { | ||
id: generateId(), | ||
role: "assistant", | ||
text: `Sorry, I encountered an error: ${error instanceof Error ? error.message : 'Unknown error'}`, | ||
timestamp: Date.now(), | ||
}; | ||
newMessages.push(errorMessage); | ||
await actions.updateMessages(state.currentThreadId, newMessages); | ||
} finally { | ||
setIsRunning(false); | ||
} | ||
}; | ||
// Thread list adapter for managing multiple threads | ||
const threadListAdapter: ExternalStoreThreadListAdapter = { | ||
threadId: state.currentThreadId, | ||
threads: state.threadList.filter((t): t is RegularThreadData => t.status === "regular"), | ||
archivedThreads: state.threadList.filter((t): t is ArchivedThreadData => t.status === "archived"), | ||
onSwitchToNewThread: async () => { | ||
const threadId = await actions.createThread("New Chat"); | ||
actions.setCurrentThread(threadId); | ||
}, | ||
onSwitchToThread: (threadId) => { | ||
actions.setCurrentThread(threadId); | ||
}, | ||
onRename: async (threadId, newTitle) => { | ||
await actions.updateThread(threadId, { title: newTitle }); | ||
}, | ||
onArchive: async (threadId) => { | ||
await actions.updateThread(threadId, { status: "archived" }); | ||
}, | ||
onDelete: async (threadId) => { | ||
await actions.deleteThread(threadId); | ||
}, | ||
}; | ||
const runtime = useExternalStoreRuntime({ | ||
messages: currentMessages, | ||
setMessages: (messages) => { | ||
actions.updateMessages(state.currentThreadId, messages); | ||
}, | ||
convertMessage, | ||
isRunning, | ||
onNew, | ||
onEdit, | ||
adapters: { | ||
threadList: threadListAdapter, | ||
}, | ||
}); | ||
if (!state.isInitialized) { | ||
return <div>Loading...</div>; | ||
} | ||
return ( | ||
<AssistantRuntimeProvider runtime={runtime}> | ||
<ChatContainer> | ||
<ThreadList /> | ||
<Thread /> | ||
</ChatContainer> | ||
</AssistantRuntimeProvider> | ||
); | ||
} | ||
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.