Skip to content

Commit ac38c66

Browse files
iamfaranraheeliftikhar5
authored andcommitted
add system prompt and improve edit UI
1 parent 5d88dbe commit ac38c66

File tree

5 files changed

+113
-49
lines changed

5 files changed

+113
-49
lines changed

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

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { createChatStorage } from "./utils/storageFactory";
1515
import { QueryHandler, createMessageHandler } from "./handlers/messageHandlers";
1616
import { useMemo, useRef, useEffect } from "react";
1717
import { changeChildAction } from "lowcoder-core";
18+
import { ChatMessage } from "./types/chatTypes";
1819

1920
import "@assistant-ui/styles/index.css";
2021
import "@assistant-ui/styles/markdown.css";
@@ -74,6 +75,30 @@ export const ChatEventHandlerControl = eventHandlerControl(ChatEventOptions);
7475
// SIMPLIFIED CHILDREN MAP - WITH EVENT HANDLERS
7576
// ============================================================================
7677

78+
79+
export function addSystemPromptToHistory(
80+
conversationHistory: ChatMessage[],
81+
systemPrompt: string
82+
): Array<{ role: string; content: string; timestamp: number }> {
83+
// Format conversation history for use in queries
84+
const formattedHistory = conversationHistory.map(msg => ({
85+
role: msg.role,
86+
content: msg.text,
87+
timestamp: msg.timestamp
88+
}));
89+
90+
// Create system message (always exists since we have default)
91+
const systemMessage = [{
92+
role: "system" as const,
93+
content: systemPrompt,
94+
timestamp: Date.now() - 1000000 // Ensure it's always first chronologically
95+
}];
96+
97+
// Return complete history with system prompt prepended
98+
return [...systemMessage, ...formattedHistory];
99+
}
100+
101+
77102
function generateUniqueTableName(): string {
78103
return `chat${Math.floor(1000 + Math.random() * 9000)}`;
79104
}
@@ -117,7 +142,6 @@ const ChatTmpComp = new UICompBuilder(
117142
(props, dispatch) => {
118143

119144
const uniqueTableName = useRef<string>();
120-
121145
// Generate unique table name once (with persistence)
122146
if (!uniqueTableName.current) {
123147
// Use persisted name if exists, otherwise generate new one
@@ -146,7 +170,7 @@ const ChatTmpComp = new UICompBuilder(
146170
return new QueryHandler({
147171
chatQuery: props.chatQuery.value,
148172
dispatch,
149-
streaming: props.streaming
173+
streaming: props.streaming,
150174
});
151175
} else if (handlerType === "n8n") {
152176
return createMessageHandler("n8n", {
@@ -168,7 +192,7 @@ const ChatTmpComp = new UICompBuilder(
168192
props.modelHost,
169193
props.systemPrompt,
170194
props.streaming,
171-
dispatch
195+
dispatch,
172196
]);
173197

174198
// Handle message updates for exposed variable
@@ -179,21 +203,23 @@ const ChatTmpComp = new UICompBuilder(
179203
};
180204

181205
// Handle conversation history updates for exposed variable
182-
const handleConversationUpdate = (conversationHistory: any[]) => {
183-
// Format conversation history for use in queries
184-
const formattedHistory = conversationHistory.map(msg => ({
185-
role: msg.role,
186-
content: msg.text,
187-
timestamp: msg.timestamp
188-
}));
189-
dispatch(changeChildAction("conversationHistory", JSON.stringify(formattedHistory), false));
190-
191-
// Trigger messageReceived event when bot responds
192-
const lastMessage = conversationHistory[conversationHistory.length - 1];
193-
if (lastMessage && lastMessage.role === 'assistant') {
194-
props.onEvent("messageReceived");
195-
}
196-
};
206+
// Handle conversation history updates for exposed variable
207+
const handleConversationUpdate = (conversationHistory: any[]) => {
208+
// Use utility function to create complete history with system prompt
209+
const historyWithSystemPrompt = addSystemPromptToHistory(
210+
conversationHistory,
211+
props.systemPrompt
212+
);
213+
214+
// Expose the complete history (with system prompt) for use in queries
215+
dispatch(changeChildAction("conversationHistory", JSON.stringify(historyWithSystemPrompt), false));
216+
217+
// Trigger messageReceived event when bot responds
218+
const lastMessage = conversationHistory[conversationHistory.length - 1];
219+
if (lastMessage && lastMessage.role === 'assistant') {
220+
props.onEvent("messageReceived");
221+
}
222+
};
197223

198224
// Cleanup on unmount
199225
useEffect(() => {
@@ -226,5 +252,5 @@ const ChatTmpComp = new UICompBuilder(
226252

227253
export const ChatComp = withExposingConfigs(ChatTmpComp, [
228254
new NameConfig("currentMessage", "Current user message"),
229-
new NameConfig("conversationHistory", "Full conversation history as JSON array"),
255+
new NameConfig("conversationHistory", "Full conversation history as JSON array (includes system prompt for API calls)"),
230256
]);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export function ChatCoreMain({
127127
setIsRunning(true);
128128

129129
try {
130+
130131
// Use the message handler (no more complex logic here!)
131132
const response = await messageHandler.sendMessage(userMessage.text);
132133

client/packages/lowcoder/src/comps/comps/chatComp/components/assistant-ui/thread-list.tsx

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import type { FC } from "react";
2+
import { useState } from "react";
23
import {
34
ThreadListItemPrimitive,
45
ThreadListPrimitive,
6+
useThreadListItem,
57
} from "@assistant-ui/react";
68
import { PencilIcon, PlusIcon, Trash2Icon } from "lucide-react";
7-
89
import { TooltipIconButton } from "./tooltip-icon-button";
910
import { useThreadListItemRuntime } from "@assistant-ui/react";
10-
import { Button, Flex } from "antd";
11+
import { Button, Flex, Input } from "antd";
1112

1213
import styled from "styled-components";
13-
import { useChatContext } from "../context/ChatContext";
1414

1515
const StyledPrimaryButton = styled(Button)`
1616
// padding: 20px;
@@ -44,12 +44,23 @@ const ThreadListItems: FC = () => {
4444
};
4545

4646
const ThreadListItem: FC = () => {
47+
const [editing, setEditing] = useState(false);
48+
4749
return (
4850
<ThreadListItemPrimitive.Root className="aui-thread-list-item">
4951
<ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger">
50-
<ThreadListItemTitle />
52+
{editing ? (
53+
<ThreadListItemEditInput
54+
onFinish={() => setEditing(false)}
55+
/>
56+
) : (
57+
<ThreadListItemTitle />
58+
)}
5159
</ThreadListItemPrimitive.Trigger>
52-
<ThreadListItemRename />
60+
<ThreadListItemRename
61+
onStartEdit={() => setEditing(true)}
62+
editing={editing}
63+
/>
5364
<ThreadListItemDelete />
5465
</ThreadListItemPrimitive.Root>
5566
);
@@ -78,37 +89,57 @@ const ThreadListItemDelete: FC = () => {
7889
};
7990

8091

81-
const ThreadListItemRename: FC = () => {
82-
const runtime = useThreadListItemRuntime();
92+
93+
const ThreadListItemEditInput: FC<{ onFinish: () => void }> = ({ onFinish }) => {
94+
const threadItem = useThreadListItem();
95+
const threadRuntime = useThreadListItemRuntime();
8396

84-
const handleClick = async () => {
85-
// runtime doesn't expose a direct `title` prop; read it from its state
86-
let current = "";
87-
try {
88-
// getState is part of the public runtime surface
89-
current = (runtime.getState?.() as any)?.title ?? "";
90-
} catch {
91-
// fallback – generate a title if the runtime provides a helper
92-
if (typeof (runtime as any).generateTitle === "function") {
93-
// generateTitle(threadId) in older builds, generateTitle() in newer ones
94-
current = (runtime as any).generateTitle((runtime as any).threadId ?? undefined);
95-
}
97+
const currentTitle = threadItem?.title || "New Chat";
98+
99+
const handleRename = async (newTitle: string) => {
100+
if (!newTitle.trim() || newTitle === currentTitle){
101+
onFinish();
102+
return;
96103
}
97-
98-
const next = prompt("Rename thread", current)?.trim();
99-
if (next && next !== current) {
100-
await runtime.rename(next);
104+
105+
try {
106+
await threadRuntime.rename(newTitle);
107+
onFinish();
108+
} catch (error) {
109+
console.error("Failed to rename thread:", error);
101110
}
102111
};
103112

113+
return (
114+
<Input
115+
size="small"
116+
defaultValue={currentTitle}
117+
onBlur={(e) => handleRename(e.target.value)}
118+
onPressEnter={(e) => handleRename((e.target as HTMLInputElement).value)}
119+
onKeyDown={(e) => {
120+
if (e.key === 'Escape') onFinish();
121+
}}
122+
autoFocus
123+
style={{ fontSize: '14px', padding: '2px 8px' }}
124+
/>
125+
);
126+
};
127+
128+
129+
const ThreadListItemRename: FC<{ onStartEdit: () => void; editing: boolean }> = ({
130+
onStartEdit,
131+
editing
132+
}) => {
133+
if (editing) return null;
134+
104135
return (
105136
<TooltipIconButton
106-
tooltip="Rename thread"
107137
variant="ghost"
108-
onClick={handleClick}
109-
className="aui-thread-list-item-rename"
138+
tooltip="Rename thread"
139+
onClick={onStartEdit}
110140
>
111141
<PencilIcon />
112142
</TooltipIconButton>
113143
);
114-
};
144+
};
145+

client/packages/lowcoder/src/comps/comps/chatComp/handlers/messageHandlers.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class QueryHandler implements MessageHandler {
5555
constructor(private config: QueryHandlerConfig) {}
5656

5757
async sendMessage(message: string): Promise<MessageResponse> {
58-
const { chatQuery, dispatch } = this.config;
58+
const { chatQuery, dispatch} = this.config;
5959

6060
// If no query selected or dispatch unavailable, return mock response
6161
if (!chatQuery || !dispatch) {
@@ -64,17 +64,22 @@ export class QueryHandler implements MessageHandler {
6464
}
6565

6666
try {
67+
6768
const result: any = await getPromiseAfterDispatch(
6869
dispatch,
6970
routeByNameAction(
7071
chatQuery,
7172
executeQueryAction({
72-
// Send the user prompt as variable named 'prompt' by default
73-
args: { prompt: { value: message } },
73+
// Send both individual prompt and full conversation history
74+
args: {
75+
prompt: { value: message },
76+
},
7477
})
7578
)
7679
);
7780

81+
console.log("QUERY RESULT", result);
82+
7883
return result.message
7984
} catch (e: any) {
8085
throw new Error(e?.message || "Query execution failed");

client/packages/lowcoder/src/comps/comps/chatComp/types/chatTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export interface ChatMessage {
6666
chatQuery: string;
6767
dispatch: any;
6868
streaming?: boolean;
69+
systemPrompt?: string;
6970
}
7071

7172
// ============================================================================

0 commit comments

Comments
 (0)