Skip to content

Commit

Permalink
feat: multi agent components
Browse files Browse the repository at this point in the history
  • Loading branch information
2976151305 committed Jan 17, 2024
1 parent 674104e commit 4a8f016
Show file tree
Hide file tree
Showing 24 changed files with 831 additions and 63 deletions.
21 changes: 14 additions & 7 deletions web/app/chat-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ interface IChatContext {
model: string;
dbParam?: string;
modelList: Array<string>;
agentList: string[];
agent: string;
dialogueList?: DialogueListResponse;
setAgent?: (val: string) => void;
setMode: (mode: ThemeMode) => void;
setAgentList?: (val: string[]) => void;
setModel: (val: string) => void;
setIsContract: (val: boolean) => void;
setIsMenuExpand: (val: boolean) => void;
Expand Down Expand Up @@ -47,8 +47,8 @@ const ChatContext = createContext<IChatContext>({
model: '',
dbParam: undefined,
dialogueList: [],
agentList: [],
setAgentList: () => {},
agent: '',
setAgent: () => {},
setModel: () => {},
setIsContract: () => {},
setIsMenuExpand: () => {},
Expand All @@ -72,7 +72,7 @@ const ChatContextProvider = ({ children }: { children: React.ReactElement }) =>
const [model, setModel] = useState<string>('');
const [isMenuExpand, setIsMenuExpand] = useState<boolean>(scene !== 'chat_dashboard');
const [dbParam, setDbParam] = useState<string>(db_param);
const [agentList, setAgentList] = useState<string[]>([]);
const [agent, setAgent] = useState<string>('');
const [history, setHistory] = useState<ChatHistoryResponse>([]);
const [docId, setDocId] = useState<number>();
const [mode, setMode] = useState<ThemeMode>('light');
Expand All @@ -91,6 +91,13 @@ const ChatContextProvider = ({ children }: { children: React.ReactElement }) =>
},
);

useEffect(() => {
if (dialogueList.length && scene === 'chat_agent') {
const agent = dialogueList.find((item) => item.conv_uid === chatId)?.select_param;
agent && setAgent(agent);
}
}, [dialogueList, scene, chatId]);

const { data: modelList = [] } = useRequest(async () => {
const [, res] = await apiInterceptors(getUsableModels());
return res ?? [];
Expand All @@ -114,10 +121,10 @@ const ChatContextProvider = ({ children }: { children: React.ReactElement }) =>
model,
dbParam: dbParam || db_param,
dialogueList,
agentList,
agent,
setAgent,
mode,
setMode,
setAgentList,
setModel,
setIsContract,
setIsMenuExpand,
Expand Down
10 changes: 8 additions & 2 deletions web/app/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,15 @@ const en = {
Retry: 'Retry',
Load_more: 'load more',
new_chat: 'New Chat',
choice_agent_tip: 'Please choose an agent',
no_context_tip: 'Please enter your question',
Terminal: 'Terminal',
} as const;

type I18nKeys = keyof typeof en;
export type I18nKeys = keyof typeof en;

export interface Resources {
translation: Record<I18nKeys, string> & { [key: string]: string };
translation: Record<I18nKeys, string>;
}

const zh: Resources['translation'] = {
Expand Down Expand Up @@ -364,6 +367,9 @@ const zh: Resources['translation'] = {
Retry: '重试',
Load_more: '加载更多',
new_chat: '创建会话',
choice_agent_tip: '请选择代理',
no_context_tip: '请输入你的问题',
Terminal: '终端',
} as const;

i18n.use(initReactI18next).init({
Expand Down
11 changes: 10 additions & 1 deletion web/client/api/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import {
PostEditorSQLRunParams,
PostSQLEditorSubmitParams,
} from '@/types/editor';
import { PostAgentHubUpdateParams, PostAgentQueryParams, PostAgentPluginResponse, PostAgentMyPluginResponse } from '@/types/agent';
import {
PostAgentHubUpdateParams,
PostAgentQueryParams,
PostAgentPluginResponse,
PostAgentMyPluginResponse,
GetDBGPTsListResponse,
} from '@/types/agent';
import {
AddKnowledgeParams,
ArgumentsParams,
Expand Down Expand Up @@ -211,6 +217,9 @@ export const postAgentUpload = (user = '', data: FormData, config?: Omit<AxiosRe
...config,
});
};
export const getDbgptsList = () => {
return GET<undefined, GetDBGPTsListResponse>('/api/v1/dbgpts/list');
};

/** chat feedback **/
export const getChatFeedBackSelect = () => {
Expand Down
6 changes: 4 additions & 2 deletions web/components/chart/autoChart/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Empty, Row, Col, Select, Tooltip } from 'antd';
import { Advice, Advisor } from '@antv/ava';
import { Chart } from '@berryv/g2-react';
import i18n from '@/app/i18n';
import i18n, { I18nKeys } from '@/app/i18n';
import { customizeAdvisor, getVisAdvices } from './advisor/pipeline';
import { useContext, useEffect, useMemo, useState } from 'react';
import { defaultAdvicesFilter } from './advisor/utils';
Expand Down Expand Up @@ -62,6 +62,8 @@ export const AutoChart = (props: AutoChartProps) => {
options={{
...spec,
theme: mode,
autoFit: true,
height: 300,
}}
/>
);
Expand All @@ -83,7 +85,7 @@ export const AutoChart = (props: AutoChartProps) => {
size={'small'}
>
{advices?.map((item) => {
const name = i18n.t(item.type);
const name = i18n.t(item.type as I18nKeys);

return (
<Option key={item.type} value={item.type}>
Expand Down
40 changes: 40 additions & 0 deletions web/components/chat/agent-content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ChatContext } from '@/app/chat-context';
import { IChatDialogueMessageSchema } from '@/types/chat';
import classNames from 'classnames';
import { memo, useContext } from 'react';
import ReactMarkdown from 'react-markdown';
import markdownComponents from './chat-content/config';
import rehypeRaw from 'rehype-raw';

interface Props {
content: IChatDialogueMessageSchema;
}

function formatMarkdownVal(val: string) {
return val.replace(/<table(\w*=[^>]+)>/gi, '<table $1>').replace(/<tr(\w*=[^>]+)>/gi, '<tr $1>');
}

function AgentContent({ content }: Props) {
const { scene } = useContext(ChatContext);

const isView = content.role === 'view';

return (
<div
className={classNames('relative w-full p-2 md:p-4 rounded-xl break-words', {
'bg-white dark:bg-[#232734]': isView,
'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),
})}
>
{isView ? (
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
{formatMarkdownVal(content.context)}
</ReactMarkdown>
) : (
<div className="">{content.context}</div>
)}
</div>
);
}

export default memo(AgentContent);
6 changes: 3 additions & 3 deletions web/components/chat/chat-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getInitMessage } from '@/utils';

const ChatContainer = () => {
const searchParams = useSearchParams();
const { scene, chatId, model, setModel, history, setHistory } = useContext(ChatContext);
const { scene, chatId, model, agent, setModel, history, setHistory } = useContext(ChatContext);
const chat = useChat({});
const initMessage = (searchParams && searchParams.get('initMessage')) ?? '';

Expand Down Expand Up @@ -95,7 +95,7 @@ const ChatContainer = () => {
});
});
},
[history, chat, model],
[history, chat, chatId, model, agent, scene],
);

return (
Expand All @@ -122,7 +122,7 @@ const ChatContainer = () => {
)}
{/** chat panel */}
<div
className={classNames('flex flex-1 flex-col', {
className={classNames('flex flex-1 flex-col overflow-hidden', {
'px-0 xl:pl-4 h-2/5 w-full xl:w-auto xl:h-full border-t xl:border-t-0 xl:border-l dark:border-gray-800': scene === 'chat_dashboard',
'h-full lg:px-8': scene !== 'chat_dashboard',
})}
Expand Down
39 changes: 39 additions & 0 deletions web/components/chat/chat-content/agent-messages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import ReactMarkdown from 'react-markdown';
import markdownComponents from './config';
import { renderModelIcon } from '../header/model-selector';
import { SwapRightOutlined } from '@ant-design/icons';

interface Props {
data: {
sender: string;
receiver: string;
model: string | null;
markdown: string;
}[];
}

function AgentMessages({ data }: Props) {
if (!data || !data.length) return null;

return (
<>
{data.map((item, index) => (
<div key={index} className="rounded my-4 md:my-6">
<div className="flex items-center mb-3 text-sm">
{item.model ? renderModelIcon(item.model) : <div className="rounded-full w-6 h-6 bg-gray-100" />}
<div className="ml-2 opacity-70">
{item.sender}
<SwapRightOutlined className="mx-2 text-base" />
{item.receiver}
</div>
</div>
<div className="whitespace-normal text-sm">
<ReactMarkdown components={markdownComponents}>{item.markdown}</ReactMarkdown>
</div>
</div>
))}
</>
);
}

export default AgentMessages;
46 changes: 46 additions & 0 deletions web/components/chat/chat-content/agent-plans.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { CaretRightOutlined, CheckOutlined, ClockCircleOutlined } from '@ant-design/icons';
import { Collapse } from 'antd';
import ReactMarkdown from 'react-markdown';
import markdownComponents from './config';

interface Props {
data: {
name: string;
num: number;
status: 'complete' | 'todo';
agent: string;
markdown: string;
}[];
}

function AgentPlans({ data }: Props) {
if (!data || !data.length) return null;

return (
<Collapse
bordered
className="my-3"
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
items={data.map((item, index) => {
return {
key: index,
label: (
<div className="whitespace-normal">
<span>
{item.name} - {item.agent}
</span>
{item.status === 'complete' ? (
<CheckOutlined className="!text-green-500 ml-2" />
) : (
<ClockCircleOutlined className="!text-gray-500 ml-2" />
)}
</div>
),
children: <ReactMarkdown components={markdownComponents}>{item.markdown}</ReactMarkdown>,
};
})}
/>
);
}

export default AgentPlans;
38 changes: 38 additions & 0 deletions web/components/chat/chat-content/chart-view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Datum } from '@antv/ava';
import { Table, Tabs, TabsProps } from 'antd';
import React from 'react';
import { format } from 'sql-formatter';
import { AutoChart, BackEndChartType, getChartType } from '@/components/chart/autoChart';
import { CodePreview } from './code-preview';

function ChartView({ data, type, sql }: { data: Datum[]; type: BackEndChartType; sql: string }) {
const columns = data?.[0]
? Object.keys(data?.[0])?.map((item) => {
return {
title: item,
dataIndex: item,
key: item,
};
})
: [];
const ChartItem = {
key: 'chart',
label: 'Chart',
children: <AutoChart data={data} chartType={getChartType(type)} />,
};
const SqlItem = {
key: 'sql',
label: 'SQL',
children: <CodePreview language="sql" code={format(sql, { language: 'mysql' }) as string} />,
};
const DataItem = {
key: 'data',
label: 'Data',
children: <Table dataSource={data} columns={columns} />,
};
const TabItems: TabsProps['items'] = type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];

return <Tabs defaultActiveKey={type === 'response_table' ? 'data' : 'chart'} items={TabItems} size="small" />;
}

export default ChartView;
14 changes: 11 additions & 3 deletions web/components/chat/chat-content/code-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ import { CopyOutlined } from '@ant-design/icons';
import { oneDark, coldarkDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import copy from 'copy-to-clipboard';
import { useContext } from 'react';
import { CSSProperties, useContext } from 'react';
import { ChatContext } from '@/app/chat-context';

export function CodePreview({ code, language }: { code: string; language: string }) {
interface Props {
code: string;
language: string;
customStyle?: CSSProperties;
light?: { [key: string]: CSSProperties };
dark?: { [key: string]: CSSProperties };
}

export function CodePreview({ code, light, dark, language, customStyle }: Props) {
const { mode } = useContext(ChatContext);

return (
Expand All @@ -20,7 +28,7 @@ export function CodePreview({ code, language }: { code: string; language: string
message[success ? 'success' : 'error'](success ? 'Copy success' : 'Copy failed');
}}
/>
<SyntaxHighlighter language={language} style={mode === 'dark' ? coldarkDark : oneDark}>
<SyntaxHighlighter customStyle={customStyle} language={language} style={mode === 'dark' ? dark ?? coldarkDark : light ?? oneDark}>
{code}
</SyntaxHighlighter>
</div>
Expand Down
Loading

0 comments on commit 4a8f016

Please sign in to comment.