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

[Feat] AI Chat Component#1850

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

Open
iamfaran wants to merge32 commits intolowcoder-org:feat/assistant
base:feat/assistant
Choose a base branch
Loading
fromiamfaran:feat/chat-component
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
32 commits
Select commitHold shift + click to select a range
158de18
[feat] replace mock data with query
iamfaranJul 7, 2025
facd5e0
Merge branch 'feat/assistant' of github.com:lowcoder-org/lowcoder int…
iamfaranJul 7, 2025
c3beba5
[Feat]: make chat component flexible
iamfaranJul 7, 2025
6376c83
setup sse http query
iamfaranJul 8, 2025
f8bf78d
fix linter errors
iamfaranJul 8, 2025
0ed0fc7
setup http streaming with dummy data
iamfaranJul 8, 2025
8542399
setup frontend for ssehttpquery
iamfaranJul 9, 2025
2925949
chat component refactor
iamfaranJul 10, 2025
9d67db7
Created test case for the ApplicationApiService.
thomas37-starJul 10, 2025
f9128ea
Created test case for the ApplicationEndpointsTest.
thomas37-starJul 10, 2025
1a587df
fix workspaces updated myorg endpoint
iamfaranJul 11, 2025
53fb65d
fix orglist active org icon
iamfaranJul 11, 2025
56c1c55
Updated CustomSelector UI
kamalqureshiJul 11, 2025
d2f2a4c
Adding Tooltip to Multiselect custom tags
kamalqureshiJul 11, 2025
1de8ae6
add unique storage / expose convo history
iamfaranJul 11, 2025
34c6351
Merge pull request #1859 from iamfaran/fix/myorg-endpoint
raheeliftikhar5Jul 11, 2025
e6a450d
Merge pull request #1858 from kamalqureshi/publish_app_groups_members
raheeliftikhar5Jul 11, 2025
d6ff092
Merge pull request #1860 from kamalqureshi/custom_tag_multiselect
raheeliftikhar5Jul 11, 2025
cc2e8d5
Created test case for the ApplicationHistorySnapshotEndpoints.
thomas37-starJul 11, 2025
ab233a5
Created test case for the ApplicationHistorySnapshotEndpoints.
thomas37-starJul 11, 2025
6560014
fixed text input glitch
raheeliftikhar5Jul 12, 2025
bf68435
Adding function Calls to Timer Component
Jul 13, 2025
6c28494
add event listeners for the chat component
iamfaranJul 14, 2025
f60c3e6
Created test case for the AuthenticationEndpoints.
thomas37-starJul 14, 2025
bff7af5
add system prompt and improve edit UI
iamfaranJul 15, 2025
955650d
Merge branch 'dev' of github.com:lowcoder-org/lowcoder into feat/chat…
iamfaranJul 15, 2025
c2f66db
add docs button in chat component
iamfaranJul 15, 2025
70b8640
fix no threads infinite re render
iamfaranJul 16, 2025
dbfda30
fix table name for better queries
iamfaranJul 16, 2025
e164f09
add custom loader
iamfaranJul 16, 2025
8aa7438
add translations for the chat component
iamfaranJul 18, 2025
948ac7d
remove console logs
iamfaranJul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,8 @@ const SelectWrapper = styled.div<{ $border?: boolean }>`
padding: ${(props) => (props.$border ? "0px" : "0 0 0 12px")};
height: 100%;
align-items: center;
margin-right: 8px;
margin-right: 10px;
padding-right: 5px;
background-color: #fff;

.ant-select-selection-item {
Expand All@@ -46,9 +47,9 @@ const SelectWrapper = styled.div<{ $border?: boolean }>`
}

.ant-select-arrow {
width:20px;
height:20px;
right:8px;
width:17px;
height:17px;
right:10px;
top: 0;
bottom: 0;
margin: auto;
Expand Down
11 changes: 7 additions & 4 deletionsclient/packages/lowcoder/src/api/userApi.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,10 +63,13 @@ export type GetCurrentUserResponse = GenericApiResponse<CurrentUser>;
export interface GetMyOrgsResponse extends ApiResponse {
data: {
data: Array<{
orgId: string;
orgName: string;
createdAt?: number;
updatedAt?: number;
isCurrentOrg: boolean;
orgView: {
orgId: string;
orgName: string;
createdAt?: number;
updatedAt?: number;
};
}>;
pageNum: number;
pageSize: number;
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -274,7 +274,7 @@ function PermissionTagRender(props: CustomTagProps) {
color={value}
closable={closable}
onClose={onClose}
style={{ marginRight: 3 }}
style={{ marginRight: 3, display: "flex", alignItems: "center" }}
>
{label}
</StyledTag>
Expand Down
12 changes: 11 additions & 1 deletionclient/packages/lowcoder/src/components/ResCreatePanel.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@ import { BottomResTypeEnum } from "types/bottomRes";
import { LargeBottomResIconWrapper } from "util/bottomResUtils";
import type { PageType } from "../constants/pageConstants";
import type { SizeType } from "antd/es/config-provider/SizeContext";
import { Datasource } from "constants/datasourceConstants";
import { Datasource, QUICK_SSE_HTTP_API_ID } from "constants/datasourceConstants";
import {
QUICK_GRAPHQL_ID,
QUICK_REST_API_ID,
Expand DownExpand Up@@ -172,13 +172,22 @@ const ResButton = (props: {
compType: "streamApi",
},
},

alasql: {
label: trans("query.quickAlasql"),
type: BottomResTypeEnum.Query,
extra: {
compType: "alasql",
},
},
sseHttpApi: {
label: trans("query.quickSseHttpAPI"),
type: BottomResTypeEnum.Query,
extra: {
compType: "sseHttpApi",
dataSourceId: QUICK_SSE_HTTP_API_ID,
},
},
graphql: {
label: trans("query.quickGraphql"),
type: BottomResTypeEnum.Query,
Expand DownExpand Up@@ -339,6 +348,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
<DataSourceListWrapper $placement={placement}>
<ResButton size={buttonSize} identifier={"restApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"streamApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"sseHttpApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"alasql"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"graphql"} onSelect={onSelect} />
<DataSourceButton size={buttonSize} onClick={() => setCurlModalVisible(true)}>
Expand Down
302 changes: 257 additions & 45 deletionsclient/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,258 @@
// 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";
import { useEffect, useState } from "react";
import { changeChildAction } from "lowcoder-core";

// Build the component
let ChatTmpComp = new UICompBuilder(
chatChildrenMap,
(props, dispatch) => {
useEffect(() => {
if (Boolean(props.tableName)) return;

// Generate a unique database name for this ChatApp instance
const generateUniqueTableName = () => {
const timestamp = Date.now();
const randomId = Math.random().toString(36).substring(2, 15);
return `TABLE_${timestamp}`;
};

const tableName = generateUniqueTableName();
dispatch(changeChildAction('tableName', tableName, true));
}, [props.tableName]);

if (!props.tableName) {
return null; // Don't render until we have a unique DB name
}
return <ChatView {...props} chatQuery={props.chatQuery.value} />;
}
)
.setPropertyViewFn((children) => <ChatPropertyView children={children} />)
.build();

ChatTmpComp = class extends ChatTmpComp {
override autoHeight(): boolean {
return this.children.autoHeight.getView();
}
};

// Export the component
export const ChatComp = withExposingConfigs(ChatTmpComp, [
new NameConfig("text", "Chat component text"),
// client/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx

import { UICompBuilder } from "comps/generators";
import { NameConfig, withExposingConfigs } from "comps/generators/withExposing";
import { StringControl } from "comps/controls/codeControl";
import { arrayObjectExposingStateControl, stringExposingStateControl } from "comps/controls/codeStateControl";
import { withDefault } from "comps/generators";
import { BoolControl } from "comps/controls/boolControl";
import { dropdownControl } from "comps/controls/dropdownControl";
import QuerySelectControl from "comps/controls/querySelectControl";
import { eventHandlerControl, EventConfigType } from "comps/controls/eventHandlerControl";
import { ChatCore } from "./components/ChatCore";
import { ChatPropertyView } from "./chatPropertyView";
import { createChatStorage } from "./utils/storageFactory";
import { QueryHandler, createMessageHandler } from "./handlers/messageHandlers";
import { useMemo, useRef, useEffect } from "react";
import { changeChildAction } from "lowcoder-core";
import { ChatMessage } from "./types/chatTypes";
import { trans } from "i18n";

import "@assistant-ui/styles/index.css";
import "@assistant-ui/styles/markdown.css";

// ============================================================================
// CHAT-SPECIFIC EVENTS
// ============================================================================

export const componentLoadEvent: EventConfigType = {
label: trans("chat.componentLoad"),
value: "componentLoad",
description: trans("chat.componentLoadDesc"),
};

export const messageSentEvent: EventConfigType = {
label: trans("chat.messageSent"),
value: "messageSent",
description: trans("chat.messageSentDesc"),
};

export const messageReceivedEvent: EventConfigType = {
label: trans("chat.messageReceived"),
value: "messageReceived",
description: trans("chat.messageReceivedDesc"),
};

export const threadCreatedEvent: EventConfigType = {
label: trans("chat.threadCreated"),
value: "threadCreated",
description: trans("chat.threadCreatedDesc"),
};

export const threadUpdatedEvent: EventConfigType = {
label: trans("chat.threadUpdated"),
value: "threadUpdated",
description: trans("chat.threadUpdatedDesc"),
};

export const threadDeletedEvent: EventConfigType = {
label: trans("chat.threadDeleted"),
value: "threadDeleted",
description: trans("chat.threadDeletedDesc"),
};

const ChatEventOptions = [
componentLoadEvent,
messageSentEvent,
messageReceivedEvent,
threadCreatedEvent,
threadUpdatedEvent,
threadDeletedEvent,
] as const;

export const ChatEventHandlerControl = eventHandlerControl(ChatEventOptions);

// ============================================================================
// SIMPLIFIED CHILDREN MAP - WITH EVENT HANDLERS
// ============================================================================


export function addSystemPromptToHistory(
conversationHistory: ChatMessage[],
systemPrompt: string
): Array<{ role: string; content: string; timestamp: number }> {
// Format conversation history for use in queries
const formattedHistory = conversationHistory.map(msg => ({
role: msg.role,
content: msg.text,
timestamp: msg.timestamp
}));

// Create system message (always exists since we have default)
const systemMessage = [{
role: "system" as const,
content: systemPrompt,
timestamp: Date.now() - 1000000 // Ensure it's always first chronologically
}];

// Return complete history with system prompt prepended
return [...systemMessage, ...formattedHistory];
}


function generateUniqueTableName(): string {
return `chat${Math.floor(1000 + Math.random() * 9000)}`;
}

const ModelTypeOptions = [
{ label: trans("chat.handlerTypeQuery"), value: "query" },
{ label: trans("chat.handlerTypeN8N"), value: "n8n" },
] as const;

export const chatChildrenMap = {
// Storage
// Storage (add the hidden property here)
_internalDbName: withDefault(StringControl, ""),
// Message Handler Configuration
handlerType: dropdownControl(ModelTypeOptions, "query"),
chatQuery: QuerySelectControl, // Only used for "query" type
modelHost: withDefault(StringControl, ""), // Only used for "n8n" type
systemPrompt: withDefault(StringControl, trans("chat.defaultSystemPrompt")),
streaming: BoolControl.DEFAULT_TRUE,

// UI Configuration
placeholder: withDefault(StringControl, trans("chat.defaultPlaceholder")),

// Database Information (read-only)
databaseName: withDefault(StringControl, ""),

// Event Handlers
onEvent: ChatEventHandlerControl,

// Exposed Variables (not shown in Property View)
currentMessage: stringExposingStateControl("currentMessage", ""),
conversationHistory: stringExposingStateControl("conversationHistory", "[]"),
};

// ============================================================================
// CLEAN CHATCOMP - USES NEW ARCHITECTURE
// ============================================================================

const ChatTmpComp = new UICompBuilder(
chatChildrenMap,
(props, dispatch) => {

const uniqueTableName = useRef<string>();
// Generate unique table name once (with persistence)
if (!uniqueTableName.current) {
// Use persisted name if exists, otherwise generate new one
uniqueTableName.current = props._internalDbName || generateUniqueTableName();

// Save the name for future refreshes
if (!props._internalDbName) {
dispatch(changeChildAction("_internalDbName", uniqueTableName.current, false));
}

// Update the database name in the props for display
const dbName = `ChatDB_${uniqueTableName.current}`;
dispatch(changeChildAction("databaseName", dbName, false));
}
// Create storage with unique table name
const storage = useMemo(() =>
createChatStorage(uniqueTableName.current!),
[]
);

// Create message handler based on type
const messageHandler = useMemo(() => {
const handlerType = props.handlerType;

if (handlerType === "query") {
return new QueryHandler({
chatQuery: props.chatQuery.value,
dispatch,
streaming: props.streaming,
});
} else if (handlerType === "n8n") {
return createMessageHandler("n8n", {
modelHost: props.modelHost,
systemPrompt: props.systemPrompt,
streaming: props.streaming
});
} else {
// Fallback to mock handler
return createMessageHandler("mock", {
chatQuery: props.chatQuery.value,
dispatch,
streaming: props.streaming
});
}
}, [
props.handlerType,
props.chatQuery,
props.modelHost,
props.systemPrompt,
props.streaming,
dispatch,
]);

// Handle message updates for exposed variable
const handleMessageUpdate = (message: string) => {
dispatch(changeChildAction("currentMessage", message, false));
// Trigger messageSent event
props.onEvent("messageSent");
};

// Handle conversation history updates for exposed variable
// Handle conversation history updates for exposed variable
const handleConversationUpdate = (conversationHistory: any[]) => {
// Use utility function to create complete history with system prompt
const historyWithSystemPrompt = addSystemPromptToHistory(
conversationHistory,
props.systemPrompt
);

// Expose the complete history (with system prompt) for use in queries
dispatch(changeChildAction("conversationHistory", JSON.stringify(historyWithSystemPrompt), false));

// Trigger messageReceived event when bot responds
const lastMessage = conversationHistory[conversationHistory.length - 1];
if (lastMessage && lastMessage.role === 'assistant') {
props.onEvent("messageReceived");
}
};

// Cleanup on unmount
useEffect(() => {
return () => {
const tableName = uniqueTableName.current;
if (tableName) {
storage.cleanup();
}
};
}, []);

return (
<ChatCore
storage={storage}
messageHandler={messageHandler}
placeholder={props.placeholder}
onMessageUpdate={handleMessageUpdate}
onConversationUpdate={handleConversationUpdate}
onEvent={props.onEvent}
/>
);
}
)
.setPropertyViewFn((children) => <ChatPropertyView children={children} />)
.build();

// ============================================================================
// EXPORT WITH EXPOSED VARIABLES
// ============================================================================

export const ChatComp = withExposingConfigs(ChatTmpComp, [
new NameConfig("currentMessage", "Current user message"),
new NameConfig("conversationHistory", "Full conversation history as JSON array (includes system prompt for API calls)"),
new NameConfig("databaseName", "Database name for SQL queries (ChatDB_<componentName>)"),
]);
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp