Skip to content

Make Chat Component Flexible #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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: feat/assistant
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 12 additions & 31 deletions client/packages/lowcoder/src/comps/comps/chatComp/chatComp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,24 @@ 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} />;
}
const ChatTmpComp = new UICompBuilder(
chatChildrenMap,
(props, dispatch) => (
<ChatView
{...props}
chatQuery={props.chatQuery.value}
currentMessage={props.currentMessage.value}
dispatch={dispatch}
/>
)
)
.setPropertyViewFn((children) => <ChatPropertyView children={children} />)
.build();

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

// Export the component
// Export the component with exposed variables
export const ChatComp = withExposingConfigs(ChatTmpComp, [
new NameConfig("text", "Chat component text"),
new NameConfig("currentMessage", "Current user message"),
]);
31 changes: 16 additions & 15 deletions client/packages/lowcoder/src/comps/comps/chatComp/chatCompTypes.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
// client/packages/lowcoder/src/comps/comps/chatComp/chatCompTypes.ts
import { StringControl, NumberControl } from "comps/controls/codeControl";
import { 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 { AutoHeightControl } from "@lowcoder-ee/comps/controls/autoHeightControl";

// Model type dropdown options
const ModelTypeOptions = [
{ label: "Direct LLM", value: "direct-llm" },
{ label: "n8n Workflow", value: "n8n" },
{ label: "Query", value: "query" },
] as const;

export const chatChildrenMap = {
text: withDefault(StringControl, "Chat Component Placeholder"),
modelType: dropdownControl(ModelTypeOptions, "direct-llm"),
chatQuery: QuerySelectControl,
currentMessage: stringExposingStateControl("currentMessage", ""),
modelType: dropdownControl(ModelTypeOptions, "query"),
modelHost: withDefault(StringControl, ""),
streaming: BoolControl.DEFAULT_TRUE,
systemPrompt: withDefault(StringControl, "You are a helpful assistant."),
agent: BoolControl,
maxInteractions: withDefault(NumberControl, 10),
chatQuery: QuerySelectControl,
autoHeight: AutoHeightControl,
tableName: withDefault(StringControl, ""),
tableName: withDefault(StringControl, "default"),
};

export type ChatCompProps = {
text?: string;
chatQuery?: string;
modelType?: string;
streaming?: boolean;
systemPrompt?: string;
agent?: boolean;
maxInteractions?: number;
modelHost?: string;
autoHeight?: boolean;
tableName?: string;
text: string;
chatQuery: string;
currentMessage: string;
modelType: string;
modelHost: string;
streaming: boolean;
systemPrompt: string;
agent: boolean;
maxInteractions: number;
tableName: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,27 @@ export const ChatPropertyView = React.memo((props: any) => {
const { children } = props;

return (
<>
<Section name={sectionNames.basic}>
{children.modelType.propertyView({ label: "Model Type" })}
{children.modelHost.propertyView({ label: "Model Host" })}
{/* {children.text.propertyView({ label: "Text" })}
{children.chatQuery.propertyView({ label: "Chat Query" })} */}
{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>
<Section name={sectionNames.layout}>
{children.autoHeight.propertyView({ label: trans("prop.height") })}
</Section>
</>
<Section name={sectionNames.basic}>
{children.text.propertyView({ label: "Text" })}
{children.chatQuery.propertyView({ label: "Chat Query" })}
{children.currentMessage.propertyView({
label: "Current Message (Dynamic)",
placeholder: "Shows the current user message",
disabled: true
})}
{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>
);
});

Expand Down
39 changes: 36 additions & 3 deletions client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,46 @@
// client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx
import React from "react";
import React, { useMemo } from "react";
import { ChatCompProps } from "./chatCompTypes";
import { CompAction } from "lowcoder-core";
import { ChatApp } from "./components/ChatApp";
import { createChatStorage } from './utils/chatStorageFactory';

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

export const ChatView = React.memo((props: ChatCompProps) => {
return <ChatApp {...props} />;
// Extend the props we receive so we can forward the redux dispatch
interface ChatViewProps extends ChatCompProps {
dispatch?: (action: CompAction<any>) => void;
}

export const ChatView = React.memo((props: ChatViewProps) => {
const {
chatQuery,
currentMessage,
dispatch,
modelType,
modelHost,
systemPrompt,
streaming,
tableName
} = props;

// Create storage instance based on tableName
const storage = useMemo(() => createChatStorage(tableName || "default"), [tableName]);

return (
<ChatApp
chatQuery={chatQuery}
currentMessage={currentMessage}
dispatch={dispatch}
modelType={modelType}
modelHost={modelHost}
systemPrompt={systemPrompt}
streaming={streaming}
tableName={tableName}
storage={storage}
/>
);
});

ChatView.displayName = 'ChatView';
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
import { ChatProvider } from "./context/ChatContext";
import { ChatMain } from "./ChatMain";
import { ChatCompProps } from "../chatCompTypes";
import { useEffect, useState } from "react";
import { CompAction } from "lowcoder-core";
import { createChatStorage } from "../utils/chatStorageFactory";

export function ChatApp(props: ChatCompProps) {
if (!Boolean(props.tableName)) {
return null; // Don't render until we have a unique DB name
}

interface ChatAppProps {
chatQuery: string;
currentMessage: string;
dispatch?: (action: CompAction<any>) => void;
modelType: string;
modelHost?: string;
systemPrompt?: string;
streaming?: boolean;
tableName: string;
storage: ReturnType<typeof createChatStorage>;
}

export function ChatApp({
chatQuery,
currentMessage,
dispatch,
modelType,
modelHost,
systemPrompt,
streaming,
tableName,
storage
}: ChatAppProps) {
return (
<ChatProvider>
<ChatMain {...props} />
<ChatProvider storage={storage}>
<ChatMain
chatQuery={chatQuery}
currentMessage={currentMessage}
dispatch={dispatch}
modelType={modelType}
modelHost={modelHost}
systemPrompt={systemPrompt}
streaming={streaming}
tableName={tableName}
/>
</ChatProvider>
);
}
}
Loading
Loading